Przytoczone przez ciebie wyrażenie regularne jest strasznym bałaganem i nie sądzę, żeby ktokolwiek zgodził się z tym, że można je odczytać. Jednocześnie duża część tej brzydoty wiąże się z rozwiązaniem problemu: istnieje kilka warstw zagnieżdżania, a gramatyka adresów URL jest stosunkowo skomplikowana (z pewnością zbyt skomplikowana, aby komunikować się zwięźle w dowolnym języku). Jednak z pewnością jest prawdą, że istnieją lepsze sposoby na opisanie tego wyrażenia regularnego. Dlaczego więc nie są używane?
Głównym powodem jest bezwładność i wszechobecność. To nie wyjaśnia, dlaczego stały się tak popularne, ale teraz, gdy już są, każdy, kto zna wyrażenia regularne, może korzystać z tych umiejętności (z niewielkimi różnicami między dialektami) w stu różnych językach i dodatkowym tysiącem narzędzi programowych ( np. edytory tekstu i narzędzia wiersza poleceń). Nawiasem mówiąc, te ostatnie nie mogłyby i nie mogłyby zastosować żadnego rozwiązania, które sprowadzałoby się do pisania programów , ponieważ są one intensywnie używane przez nie-programistów.
Mimo to wyrażenia regularne są często nadużywane, to znaczy stosowane nawet wtedy, gdy inne narzędzie byłoby znacznie lepsze. Nie sądzę, żeby składnia wyrażeń regularnych była okropna . Ale jest wyraźnie lepszy w krótkich i prostych wzorach: archetypowy przykład identyfikatorów w językach podobnych do C [a-zA-Z_][a-zA-Z0-9_]*
można odczytać z absolutnym minimum znajomości wyrażeń regularnych, a kiedy ten pasek zostanie spełniony, jest on zarówno oczywisty, jak i bardzo zwięzły. Wymaganie mniejszej liczby znaków nie jest z natury złe, wręcz przeciwnie. Bycie zwięzłym to zaleta, pod warunkiem, że będziesz zrozumiały.
Są co najmniej dwa powody, dla których ta składnia wyróżnia się prostymi wzorami takimi jak te: Nie wymaga zmiany znaczenia dla większości znaków, więc czyta się stosunkowo naturalnie i używa wszystkich dostępnych interpunkcji, aby wyrazić różnorodne proste kombinatory parsowania. Być może, co najważniejsze, nie wymaga niczego do sekwencjonowania. Piszesz pierwszą rzecz, a potem następną. Porównaj to z twoim followedBy
, szczególnie gdy poniższy wzór nie jest dosłownym, ale bardziej skomplikowanym wyrażeniem.
Dlaczego więc nie udaje im się w bardziej skomplikowanych przypadkach? Widzę trzy główne problemy:
Brak możliwości abstrakcji. Gramatyki formalne, które wywodzą się z tej samej dziedziny informatyki teoretycznej co wyrażenia regularne, mają zestaw produkcji, dzięki czemu mogą nadawać nazwy pośrednim częściom wzoru:
# This is not equivalent to the regex in the question
# It's just a mock-up of what a grammar could look like
url ::= protocol? '/'? '/'? '/'? (domain_part '.')+ tld
protocol ::= letter+ ':'
...
Jak widzieliśmy powyżej, białe znaki nie mające specjalnego znaczenia są przydatne, aby umożliwić formatowanie, które jest łatwiejsze dla oczu. To samo z komentarzami. Wyrażenia regularne nie mogą tego zrobić, ponieważ spacja jest po prostu literałem ' '
. Uwaga: niektóre implementacje pozwalają na tryb „pełny”, w którym białe znaki są ignorowane i możliwe są komentarze.
Nie ma metajęzyka opisującego typowe wzorce i kombinatory. Na przykład, można napisać digit
regułę raz i używać jej w gramatyce bezkontekstowej, ale nie można zdefiniować „funkcji”, że tak powiem, która ma produkcję p
i tworzy nową produkcję, która robi z nią coś dodatkowego, na przykład tworzy produkcja rozdzielonej przecinkami listy wystąpień p
.
Proponowane przez ciebie podejście z pewnością rozwiązuje te problemy. Po prostu nie rozwiązuje ich zbyt dobrze, ponieważ zamienia się w o wiele bardziej zwięzłe, niż to konieczne. Pierwsze dwa problemy można rozwiązać, pozostając w stosunkowo prostym i zwięzłym języku specyficznym dla domeny. Trzecie, cóż ... programowe rozwiązanie wymaga oczywiście ogólnego języka programowania, ale z mojego doświadczenia wynika, że trzeci jest zdecydowanie najmniejszym z tych problemów. Niewiele wzorów ma wystarczającą liczbę wystąpień tego samego złożonego zadania, które programiści tęsknią za umiejętnością definiowania nowych kombinacji. A gdy jest to konieczne, język jest często na tyle skomplikowany, że nie może i nie powinien być analizowany z wyrażeniami regularnymi.
Istnieją rozwiązania dla tych przypadków. Istnieje około dziesięciu tysięcy bibliotek kombinatora parserów, które wykonują mniej więcej to, co proponujesz, tylko z innym zestawem operacji, często inną składnią i prawie zawsze z większą mocą analizy niż wyrażenia regularne (tj. Zajmują się językami bezkontekstowymi lub dużymi rozmiarami podzbiór tych). Są też generatory parsera, które działają zgodnie z opisanym powyżej podejściem „użyj lepszego DSL”. I zawsze istnieje możliwość ręcznego zapisania części parsowania we właściwym kodzie. Możesz nawet mieszać i dopasowywać, używając wyrażeń regularnych do prostych zadań podrzędnych i wykonując skomplikowane czynności w kodzie, wywołując wyrażenia regularne.
Nie wiem wystarczająco dużo o pierwszych latach komputerów, aby wyjaśnić, dlaczego wyrażenia regularne stały się tak popularne. Ale są tutaj, by zostać. Musisz tylko mądrze z nich korzystać i nie używać ich, gdy jest to mądrzejsze.