Jest popularny cytat Jamie Zawinski :
Niektórzy ludzie, gdy napotykają problem, myślą: „Wiem, użyję wyrażeń regularnych”. Teraz mają dwa problemy.
Jak należy rozumieć ten cytat?
Jest popularny cytat Jamie Zawinski :
Niektórzy ludzie, gdy napotykają problem, myślą: „Wiem, użyję wyrażeń regularnych”. Teraz mają dwa problemy.
Jak należy rozumieć ten cytat?
Odpowiedzi:
Niektóre technologie programowania nie są na ogół dobrze rozumiane przez programistów ( wyrażenia regularne , zmiennoprzecinkowe , Perl , AWK , IoC ... i inne ).
Mogą to być niezwykle potężne narzędzia do rozwiązywania właściwego zestawu problemów. Zwłaszcza wyrażenia regularne są bardzo przydatne do dopasowania języków regularnych. I sedno problemu: niewiele osób wie, jak opisać zwykły język (jest to część teorii / lingwistyki komputerowej, która używa zabawnych symboli - o tym można przeczytać w hierarchii Chomsky'ego ).
Jeśli masz do czynienia z tymi rzeczami, jeśli użyjesz ich źle, jest mało prawdopodobne, że rzeczywiście rozwiązałeś swój pierwotny problem. Korzystanie z wyrażeń regularnych, aby dopasować HTML (a zbyt częstym zjawiskiem) będzie oznaczać, że będzie przegap przypadki krawędzi. A teraz nadal masz pierwotny problem, którego nie rozwiązałeś, i kolejny subtelny błąd, który został wprowadzony przy użyciu niewłaściwego rozwiązania.
Nie oznacza to, że nie należy używać wyrażeń regularnych, ale raczej należy pracować, aby zrozumieć, jaki zestaw problemów można rozwiązać, a których nie można rozwiązać i rozsądnie z nich korzystać.
Kluczem do utrzymania oprogramowania jest pisanie możliwego do utrzymania kodu. Używanie wyrażeń regularnych może być sprzeczne z tym celem. Podczas pracy z wyrażeniami regularnymi napisałeś mini komputer (w szczególności niedeterministyczny automat skończony ) w specjalnym języku specyficznym dla domeny. Łatwo jest napisać odpowiednik „Witaj świecie” w tym języku i zyskać w nim szczerą pewność siebie, ale dalsza poprawa wymaga zrozumienia zwykłego języka, aby uniknąć pisania dodatkowych błędów, które mogą być bardzo trudne do zidentyfikowania i naprawienia (ponieważ nie są częścią programu, w którym znajduje się wyrażenie regularne).
Więc teraz masz nowy problem; wybrałeś narzędzie wyrażenia regularnego, aby je rozwiązać (gdy jest nieodpowiednie) i masz teraz dwa błędy, z których oba są trudniejsze do znalezienia, ponieważ są ukryte w innej warstwie abstrakcji.
Wyrażenia regularne - szczególnie nietrywialne - są potencjalnie trudne do kodowania, zrozumienia i utrzymania. Wystarczy spojrzeć na liczbę pytań na temat przepełnienia stosu oznaczonych [regex]
tam, gdzie pytający założył, że odpowiedź na ich problem jest wyrażeniem regularnym, a następnie utknęła. W wielu przypadkach problem można (i być może powinien) rozwiązać w inny sposób.
Oznacza to, że jeśli zdecydujesz się użyć wyrażenia regularnego, masz teraz dwa problemy:
Zasadniczo myślę, że ma na myśli, że powinieneś używać wyrażenia regularnego tylko wtedy, gdy nie ma innego sposobu rozwiązania twojego problemu. Innym rozwiązaniem będzie prawdopodobnie łatwiejsze kodowanie, obsługa i obsługa. Może być wolniejszy lub mniej wydajny, ale jeśli nie jest to krytyczne, najważniejszą kwestią jest łatwość konserwacji i wsparcia.
Jest to przeważnie żart, choć z odrobiną prawdy.
Istnieje kilka zadań, dla których wyrażenia regularne są doskonale dopasowane. Kiedyś zastąpiłem 500 wierszy ręcznie napisanego kodu parsera rekurencyjnego zejścia jednym wyrażeniem regularnym, którego pełne debugowanie zajęło około 10 minut. Ludzie mówią, że wyrażenia regularne są trudne do zrozumienia i debugowania, ale odpowiednio zastosowane nie są tak trudne do debugowania, jak ogromny ręcznie zaprojektowany parser. W moim przykładzie debugowanie wszystkich najważniejszych przypadków rozwiązania nieregexowego zajęło dwa tygodnie.
Parafrazując wujka Bena:
Z wielką ekspresją wiąże się wielka odpowiedzialność.
Innymi słowy, wyrażenia regularne dodają ekspresji Twojemu językowi, ale nakłada na programistę większą odpowiedzialność za wybór najbardziej czytelnego trybu wyrażania dla danego zadania.
Niektóre rzeczy początkowo wyglądają na dobre zadanie dla wyrażeń regularnych, ale nimi nie są. Na przykład wszystko z zagnieżdżonymi tokenami, takie jak HTML. Czasami ludzie używają wyrażeń regularnych, gdy prostsza metoda jest bardziej przejrzysta. Na przykład string.endsWith("ing")
jest łatwiejszy do zrozumienia niż odpowiednik wyrażenia regularnego. Czasami ludzie starają się wcisnąć duży problem w jeden regex, gdzie bardziej odpowiednie jest rozbicie go na kawałki. Czasami ludzie nie tworzą odpowiednich abstrakcji, powtarzając wielokrotnie wyrażenie regularne zamiast tworzyć dobrze nazwaną funkcję wykonującą to samo zadanie (być może zaimplementowaną wewnętrznie za pomocą wyrażenia regularnego).
Z jakiegoś powodu wyrażenia regularne mają dziwną tendencję do tworzenia martwego pola do normalnych zasad inżynierii oprogramowania, takich jak pojedyncza odpowiedzialność i DRY. Dlatego nawet ludzie, którzy je kochają, czasami uważają je za problematyczne.
Jeff Atwood przedstawia inną interpretację w poście na blogu, omawiając ten bardzo cytat: Wyrażenia regularne: teraz masz dwa problemy (dzięki Euphoric za link)
Analizując pełny tekst postów Jamiego w oryginalnym wątku z 1997 roku, znajdujemy następujące:
Natura Perla zachęca do używania wyrażeń regularnych prawie z wyłączeniem wszystkich innych technik; są zdecydowanie najbardziej „oczywistym” (przynajmniej dla ludzi, którzy nie znają lepszego) sposobem na przejście z punktu A do punktu B.
Pierwszy cytat jest zbyt pochlebny, aby można go było traktować poważnie. Ale całkowicie się z tym zgadzam. Oto, o czym Jamie starał się powiedzieć: nie to, że wyrażenia regularne są same w sobie złe, ale że nadużywanie wyrażeń regularnych jest złe.
Nawet jeśli nie w pełni zrozumieć wyrażeń regularnych, napotkasz The Golden Hammer problemu, próbując rozwiązać problem z wyrażeń regularnych, gdy byłoby łatwiejsze i bardziej jasne zrobić to samo z regularnym kodu (patrz również CodingHorror: Regex korzystania vs. nadużycie Regex ).
Istnieje inny post na blogu, który analizuje kontekst cytatu i jest bardziej szczegółowy niż Atwood: Blog Jeffrey'a Friedla: Źródło słynnego cytatu „Teraz masz dwa problemy”
Z tym cytatem dzieje się kilka rzeczy.
Cytat jest powtórzeniem wcześniejszego żart:
Za każdym razem, gdy napotyka problem, niektórzy mówią: „Użyjmy AWK”. Teraz mają dwa problemy. - D. Tilbrook
Jest to żart i prawdziwe wykopalisko, ale także sposób na podkreślenie wyrażenia regularnego jako złego rozwiązania poprzez połączenie go z innymi złymi rozwiązaniami. To świetny ha ha tylko poważny moment.
Dla mnie - pamiętam, ten cytat celowo jest otwarty na interpretację - znaczenie jest proste. Samo zapowiedź użycia wyrażenia regularnego nie rozwiązało problemu. Ponadto zwiększyłeś złożoność poznawczą kodu, dodając dodatkowy język z regułami, które różnią się od języka, którego używasz.
Chociaż jest to zabawne jak żart, musisz porównać złożoność rozwiązania niebędącego wyrażeniem regularnym z złożonością rozwiązania wyrażenia regularnego + dodatkową złożonością dołączania wyrażeń regularnych. Być może warto rozwiązać problem z wyrażeniem regularnym, pomimo dodatkowych kosztów związanych z dodawaniem wyrażeń regularnych.
Wyrażenia regularne są teraz zestawem zwykłych rzeczy, które mają inną sformułowaną treść; w rzeczywistości istnieje prawdopodobieństwo, że jest to więcej niż ten element tekstu, ale niefortunnie, że mają one wpływ na powód, który powoduje pewne uzupełnienia.
(Wyrażenia regularne nie są gorsze do odczytania lub utrzymania niż jakakolwiek inna niesformatowana treść; rzeczywiście wyrażenie regularne jest prawdopodobnie łatwiejsze do odczytania niż ten fragment tekstu tutaj - ale niestety mają złą reputację, ponieważ niektóre implementacje nie pozwalają na formatowanie i ogólnie ludzi nie wiem, czy możesz to zrobić.)
Oto trywialny przykład:
^(?:[^,]*+,){21}[^,]*+$
Co tak naprawdę nie jest tak trudne do odczytania lub utrzymania, ale jest nawet łatwiejsze, gdy wygląda tak:
(?x) # enables comments, so this whole block can be used in a regex.
^ # start of string
(?: # start non-capturing group
[^,]*+ # as many non-commas as possible, but none required
, # a comma
) # end non-capturing group
{21} # 21 of previous entity (i.e. the group)
[^,]*+ # as many non-commas as possible, but none required
$ # end of string
To trochę przesadzony przykład (komentowanie $
jest podobne do komentowania i++
), ale najwyraźniej nie powinno być problemu z czytaniem, rozumieniem i utrzymywaniem tego.
Tak długo, jak masz jasność, kiedy wyrażenia regularne są odpowiednie, a kiedy są złym pomysłem, nie ma w nich nic złego, a większość razy cytat JWZ tak naprawdę nie ma zastosowania.
*+
? Jak to się różni (funkcjonalnie) od sprawiedliwego *
?
*+
tym przypadku dosłownie nie ma sensu ; wszystko jest zakotwiczone i może być dopasowane w jednym przejściu przez automat, który może liczyć do 22. Prawidłowy modyfikator w tych zestawach bez przecinków jest po prostu stary *
. (Co więcej, tutaj nie powinno być różnic między chciwymi i niechcianymi algorytmami dopasowywania. Jest to niezwykle prosty przypadek.)
Oprócz odpowiedzi ChrisF - że wyrażenia regularne „są trudne do kodowania, rozumienia i utrzymywania”, jest jeszcze gorzej: są wystarczająco potężne, aby nakłonić ludzi do próby użycia ich do analizy rzeczy, których nie potrafią, na przykład HTML. Zobacz liczne pytania na temat SO dotyczące „jak parsować HTML?” Na przykład najbardziej epicka odpowiedź w całym SO!
Wyrażenia regularne są bardzo potężne, ale mają jeden mały i jeden duży problem; są trudne do napisania i prawie niemożliwe do odczytania.
W najlepszym przypadku użycie wyrażenia regularnego rozwiązuje problem, więc masz tylko problem z obsługą skomplikowanego kodu. Jeśli wyrażenie regularne nie jest odpowiednie, masz zarówno pierwotny problem, jak i problem z nieczytelnym kodem, który nie działa.
Czasami wyrażenia regularne są nazywane kodem tylko do zapisu. W obliczu wyrażenia regularnego, które wymaga naprawy, często szybciej jest zaczynać od zera niż próbować zrozumieć wyrażenie.
Problem polega na tym, że regex jest skomplikowaną bestią, a problem rozwiązujesz tylko wtedy, gdy używasz regex doskonale. Jeśli tego nie zrobisz, będziesz mieć 2 problemy: twój oryginalny problem i regex.
Twierdzisz, że potrafi wykonać setkę wierszy kodu, ale możesz również argumentować, że 100 wierszy przejrzystego, zwięzłego kodu jest lepsze niż jeden wiersz wyrażenia regularnego.
Jeśli potrzebujesz na to dowodu: możesz sprawdzić ten SO Classic lub po prostu przeczesać tag SO Regex
Znaczenie składa się z dwóch części:
Gdy poprosisz o to w 2014 r., Interesujące byłoby skoncentrowanie się na ideologiach języków programowania w kontekście z 1997 r. W porównaniu do dzisiejszego kontekstu. Nie będę wchodził w tę debatę, ale opinie na temat Perla i samego Perla uległy znacznej zmianie.
Jednak, aby pozostać w kontekście z 2013 r. ( De l'eau a coulé sous les ponts depuis), sugerowałbym skupienie się na rekonstrukcji cytatów za pomocą słynnego komiksu XKCD, który jest bezpośrednim cytatem komiksu Jamiego Zawińskiego :
Najpierw miałem problemy, aby zrozumieć ten komiks, ponieważ było to odniesienie do Zawinski cytatem, a cytat piosenki Jay-Z pieśnią, a odniesienie GNU program --help -z
flagą 2 , tak, to było zbyt wiele kulturę mi to zrozumieć.
Wiedziałem, że to dobra zabawa, czułem to, ale tak naprawdę nie wiedziałem dlaczego. Ludzie często żartują na temat Perla i wyrażeń regularnych, zwłaszcza, że nie jest to najmodniejszy język programowania, nie bardzo wiedzą, dlaczego to ma być zabawą ... Może dlatego, że manipulatorzy Perla robią głupie rzeczy .
Tak więc początkowy cytat wydaje się sarkastycznym żartem opartym na rzeczywistych problemach (ból?) Spowodowanych programowaniem za pomocą narzędzi, które boli. Tak jak młot może zranić murarza, programowanie za pomocą narzędzi, które nie są tymi, które wybrałby deweloper, gdyby mógł zranić (mózg, uczucia). Czasami zdarzają się wielkie debaty na temat tego, które narzędzie jest najlepsze, ale jest to prawie bezwartościowe, ponieważ jest to problem Twojego gustu lub gustu zespołu programistycznego , powodów kulturowych lub ekonomicznych . Kolejny doskonały komiks XKCD na ten temat:
Rozumiem, że ludzie odczuwają ból związany z wyrażeniami regularnymi i wierzą, że inne narzędzie lepiej nadaje się do tego, do czego przeznaczone są wyrażenia regularne. Kiedy @ karl-bielefeldt odpowiada na twoje pytanie z wielką ekspresją, pojawia się wielka odpowiedzialność , a wyrażenia regularne są tym szczególnie zaniepokojone. Jeśli programista nie dba o to, jak s-on radzi sobie z wyrażeniami regularnymi, w końcu będzie to stanowić problem dla ludzi, którzy utrzymają kod później.
Kończę tę odpowiedzią na temat rekonstrukcji cytatów cytatem przedstawiającym typowy przykład z Perl Best Practices Damiana Conwego (książka z 2005 roku).
Wyjaśnia, że pisanie takiego wzoru:
m{'[^\\']*(?:\\.[^\\']*)*'}
... jest nie do przyjęcia bardziej niż napisanie takiego programu :
sub'x{local$_=pop;sub'_{$_>=$_[0
]?$_[1]:$"}_(1,'*')._(5,'-')._(4
,'*').$/._(6,'|').($_>9?'X':$_>8
?'/':$")._(8,'|').$/._(2,'*')._(
7,'-')._(3,'*').$/}print$/x($=).
x(10)x(++$x/10).x($x%10)while<>;
Ale można go przepisać , wciąż nie jest ładny, ale przynajmniej można go przetrwać.
# Match a single-quoted string efficiently...
m{ ' # an opening single quote
[^\\']* # any non-special chars (i.e., not backslash or single quote)
(?: # then all of...`
\\ . # any explicitly backslashed char
[^\\']* # followed by any non-special chars
)* # ...repeated zero or more times
' # a closing single quote
}x
Ten rodzaj kodu w kształcie prostokąta jest drugim problemem, a nie wyrażeniami regularnymi, które można sformatować w jasny, łatwy do utrzymania i czytelny sposób.
/* Multiply the first 10 values in an array by 2. */ for (int i = 0 /* the loop counter */; i < 10 /* continue while it is less than 10 */; ++i /* and increment it by 1 in each iteration */) { array[i] *= 2; /* double the i-th element in the array */ }
Jeśli jest jedna rzecz, której powinieneś nauczyć się z informatyki, to hierarchia Chomsky'ego . Powiedziałbym, że wszystkie problemy z wyrażeniami regularnymi wynikają z prób parsowania gramatyki bezkontekstowej. Kiedy możesz narzucić limit (lub pomyśleć, że możesz narzucić limit) na poziomy zagnieżdżenia w CFG, otrzymasz te długie i złożone wyrażenia regularne.
Wyrażenia regularne są bardziej odpowiednie do tokenizacji niż do parsowania na pełną skalę.
Ale zaskakująco duży zestaw rzeczy, które programiści muszą przeanalizować, można przeanalizować za pomocą zwykłego języka (lub, co gorsza, można go prawie parsować za pomocą zwykłego języka i jeśli napiszesz tylko trochę więcej kodu ...).
Więc jeśli ktoś jest przyzwyczajony do „aha, muszę rozróżniać tekst, użyję wyrażenia regularnego”, łatwo jest zejść tą drogą, gdy potrzebujesz czegoś, co jest bliższe automatowi push-down, parserowi CFG lub nawet mocniejsze gramatyki. To zwykle kończy się łzami.
Więc myślę, że cytat nie jest tak trzaskającym wyrażeniem regularnym, mają swoje zastosowanie (i dobrze wykorzystane, są naprawdę bardzo przydatne), ale nadmierne poleganie na wyrażeniach regularnych (lub, w szczególności, ich bezkrytyczny wybór) .
jwz jest po prostu szalony z tym cytatem. wyrażenia regularne nie różnią się niczym od żadnej funkcji językowej - łatwe do spieprzenia, trudne do eleganckiego użycia, czasem mocne, czasem nieodpowiednie, często dobrze udokumentowane, często przydatne.
to samo można powiedzieć o arytmetyki zmiennoprzecinkowej, zamknięciach, orientacji obiektowej, asynchronicznych We / Wy lub cokolwiek innego, co można nazwać. jeśli nie wiesz, co robisz, języki programowania mogą cię zasmucić.
jeśli uważasz, że wyrażenia regularne są trudne do odczytania, spróbuj odczytać równoważną implementację analizatora składni w celu wykorzystania danego wzorca. często wyrażenia regularne wygrywają, ponieważ są bardziej zwarte niż pełne parsery ... aw większości języków są również szybsze.
nie zniechęcaj się do używania wyrażeń regularnych (lub jakiejkolwiek innej funkcji językowej), ponieważ autopromocyjny bloger wydaje niekwalifikowane oświadczenia. wypróbuj rzeczy dla siebie i sprawdź, co Ci odpowiada.
Moja ulubiona, dogłębna odpowiedź na to pytanie została podana przez słynnego Roba Pike'a w poście na blogu skopiowanym z wewnętrznego komentarza Google: http://commandcenter.blogspot.ch/2011/08/regular-expressions-in-lexing- i.html
Podsumowując, nie jest tak, że są złe , ale często są używane do zadań, dla których niekoniecznie są odpowiednie, szczególnie jeśli chodzi o leksykację i analizę niektórych danych wejściowych.
Wyrażenia regularne są trudne do napisania, trudne do napisania i mogą być drogie w porównaniu z innymi technologiami ... Z drugiej strony Lexery są dość łatwe do napisania poprawnie (jeśli nie tak kompaktowo) i bardzo łatwe do przetestowania. Zastanów się nad znalezieniem alfanumerycznych identyfikatorów. Nie jest trudno napisać wyrażenie regularne (coś w stylu „[a-ZA-Z _] [a-ZA-Z_0-9] *”), ale naprawdę nie jest trudniej napisać jako prostą pętlę. Wydajność pętli będzie jednak znacznie wyższa i będzie wymagać znacznie mniej kodu pod okładkami. Biblioteka wyrażeń regularnych to wielka rzecz. Używanie jednego do parsowania identyfikatorów jest jak używanie Ferrari, aby przejść do sklepu po mleko.
Mówi o wiele więcej, argumentując, że wyrażenia regularne są przydatne np. W jednorazowym dopasowywaniu wzorców w edytorach tekstowych, ale rzadko powinny być używane w skompilowanym kodzie i tak dalej. Warto przeczytać.
Jest to związane z epigramem Alana Perlisa # 34:
Łańcuch jest surową strukturą danych i wszędzie tam, gdzie jest przekazywany, proces kopiowania jest bardzo duży. Jest to idealny pojazd do ukrywania informacji.
Jeśli więc wybierzesz ciąg znaków jako strukturę danych (i, oczywiście, kod oparty na wyrażeniach regularnych jako algorytmy do manipulowania nim), masz problem, nawet jeśli działa: zły projekt wokół niewłaściwej reprezentacji danych, który jest trudny do rozszerzyć i nieefektywne.
Jednak często to nie działa: pierwotny problem nie został rozwiązany, więc w takim przypadku masz dwa problemy.
Regeksy są szeroko stosowane do szybkiego i brudnego parsowania tekstu. Są doskonałym narzędziem do wyrażania wzorów, które są nieco bardziej złożone niż zwykłe dopasowanie ciągów.
Jednak, gdy wyrażenia regularne stają się coraz bardziej złożone, problemy serwerowe podnoszą głowę.
Dlatego zbyt łatwo jest zacząć od problemu z przetwarzaniem tekstu, zastosować do niego wyrażenia regularne i skończyć z dwoma problemami, pierwotnym problemem, który próbowałeś rozwiązać i radzeniem sobie z wyrażeniami regularnymi, które próbujesz rozwiązać (ale nie rozwiązujesz poprawnie) oryginalny problem.