C'è una citazione popolare di Jamie Zawinski :
Alcune persone, di fronte a un problema, pensano "Lo so, userò espressioni regolari". Ora hanno due problemi.
Come dovrebbe essere intesa questa citazione?
C'è una citazione popolare di Jamie Zawinski :
Alcune persone, di fronte a un problema, pensano "Lo so, userò espressioni regolari". Ora hanno due problemi.
Come dovrebbe essere intesa questa citazione?
Risposte:
Alcune tecnologie di programmazione non sono generalmente ben comprese dai programmatori ( espressioni regolari , virgola mobile , Perl , AWK , IoC ... e altri ).
Questi possono essere strumenti straordinariamente potenti per risolvere il giusto insieme di problemi. Le espressioni regolari in particolare sono molto utili per abbinare le lingue regolari. E c'è il nocciolo del problema: poche persone sanno come descrivere un linguaggio regolare (fa parte della teoria / linguistica dell'informatica che usa simboli divertenti - puoi leggerlo nella gerarchia di Chomsky ).
Quando si affrontano queste cose, se le si utilizza in modo errato è improbabile che tu abbia effettivamente risolto il problema originale. Usando un'espressione regolare per abbinare HTML (un evento fin troppo comune) significa che si avrà perdere casi limite. E ora, hai ancora il problema originale che non hai risolto e un altro bug sottile che è stato introdotto utilizzando la soluzione sbagliata.
Questo non vuol dire che le espressioni regolari non dovrebbero essere usate, ma piuttosto che uno dovrebbe lavorare per capire quale sia l'insieme di problemi che possono risolvere e non possono risolvere e usarli con giudizio.
La chiave per mantenere il software sta scrivendo codice gestibile. L'uso di espressioni regolari può essere contrario a tale obiettivo. Quando lavori con espressioni regolari, hai scritto un mini computer (in particolare un automa a stati finiti non deterministico ) in un linguaggio specifico specifico del dominio. È facile scrivere l'equivalente di "Hello world" in questa lingua e acquisire rudimentale fiducia in essa, ma andare oltre deve essere temperato con la comprensione della lingua normale per evitare di scrivere bug aggiuntivi che possono essere molto difficili da identificare e correggere (perché non fanno parte del programma in cui si trova l'espressione regolare).
Quindi ora hai un nuovo problema; hai scelto lo strumento dell'espressione regolare per risolverlo (quando è inappropriato), e ora hai due bug, entrambi i quali sono più difficili da trovare, perché sono nascosti in un altro livello di astrazione.
Le espressioni regolari, in particolare quelle non banali, sono potenzialmente difficili da codificare, comprendere e mantenere. Devi solo guardare il numero di domande su Stack Overflow taggate in [regex]
cui l'interrogatore ha assunto che la risposta al loro problema sia una regex e che successivamente si sia bloccato. In molti casi il problema può (e forse dovrebbe) essere risolto in modo diverso.
Ciò significa che, se decidi di usare un regex, ora hai due problemi:
Fondamentalmente, penso che significhi che dovresti usare una regex solo se non c'è altro modo di risolvere il tuo problema. Un'altra soluzione sarà probabilmente più semplice da programmare, gestire e supportare. Potrebbe essere più lento o meno efficiente, ma in caso contrario la facilità di manutenzione e supporto dovrebbe essere la preoccupazione principale.
È soprattutto uno scherzo ironico, sebbene con un granello di verità.
Ci sono alcuni compiti per i quali le espressioni regolari si adattano perfettamente. Una volta ho sostituito 500 righe di codice parser di discesa ricorsivo scritto manualmente con un'espressione regolare che ha richiesto circa 10 minuti per eseguire il debug completo. La gente dice che le regex sono difficili da capire e da debug, ma quelle applicate in modo appropriato non sono così difficili da debug quanto un enorme parser progettato a mano. Nel mio esempio, ci sono volute due settimane per eseguire il debug di tutti i casi limite della soluzione non regex.
Tuttavia, per parafrasare lo zio Ben:
Da una grande espressività derivano grandi responsabilità.
In altre parole, le regex aggiungono espressività alla tua lingua, ma ciò attribuisce maggiore responsabilità al programmatore nella scelta della modalità di espressione più leggibile per un determinato compito.
Inizialmente alcune cose sembrano un buon compito per le espressioni regolari, ma non lo sono. Ad esempio, qualsiasi cosa con token nidificati, come HTML. A volte le persone usano un'espressione regolare quando un metodo più semplice è più chiaro. Ad esempio, string.endsWith("ing")
è più facile da capire rispetto alla regex equivalente. A volte le persone cercano di stipare un grosso problema in un'unica regex, dove è più appropriato spezzarlo. A volte le persone non riescono a creare astrazioni appropriate, ripetendo ripetutamente una regex invece di creare una funzione ben denominata per fare lo stesso lavoro (forse implementata internamente con una regex).
Per qualche ragione, le regex hanno una strana tendenza a creare un punto cieco ai normali principi di ingegneria del software come la responsabilità singola e il DRY. Ecco perché a volte anche le persone che li amano trovano problematiche.
Jeff Atwood fa emergere un'interpretazione diversa in un post sul blog discutendo proprio questa citazione: Espressioni regolari: ora hai due problemi (grazie a Euphoric per il link)
Analizzando il testo completo dei post di Jamie nella discussione originale del 1997, troviamo quanto segue:
La natura di Perl incoraggia l'uso di espressioni regolari quasi all'esclusione di tutte le altre tecniche; sono di gran lunga il modo più "ovvio" (almeno per le persone che non conoscono meglio) di andare dal punto A al punto B.
La prima citazione è troppo volgare per essere presa sul serio. Ma questo, sono completamente d'accordo. Ecco il punto che Jamie stava cercando di chiarire: non che le espressioni regolari siano di per sé cattive, ma che l'abuso di espressioni regolari sia malvagio.
Anche se non comprendere appieno le espressioni regolari, si esegue in The Golden Hammer problema, cercando di risolvere un problema con le espressioni regolari, quando sarebbe stato più facile e più chiaro per fare la stessa cosa con il codice di regolare (vedi anche CodingHorror: uso Regex vs. abuso di Regex ).
C'è un altro post sul blog che esamina il contesto della citazione, e approfondisce più di Atwood: il blog di Jeffrey Friedl: fonte della famosa citazione "Ora hai due problemi"
Ci sono alcune cose in corso con questa citazione.
La citazione è una riaffermazione di uno scherzo precedente:
Ogni volta che affrontano un problema, alcune persone dicono "Consente di utilizzare AWK". Ora hanno due problemi. - D. Tilbrook
È uno scherzo e uno scavo reale, ma è anche un modo per evidenziare regex come una cattiva soluzione collegandola con altre soluzioni cattive. È un grande ah ah solo un momento serio .
Per me - intendiamoci, questa citazione è volutamente aperta all'interpretazione - il significato è diretto. Il semplice annuncio dell'utilizzo di un'espressione regolare non ha risolto il problema. Inoltre, hai aumentato la complessità cognitiva del codice aggiungendo un linguaggio aggiuntivo con regole che si distinguono da qualsiasi linguaggio tu stia utilizzando.
Anche se divertente come uno scherzo, devi confrontare la complessità di una soluzione non regex con la complessità della soluzione regex + la complessità aggiuntiva di includere regex. Potrebbe essere utile risolvere un problema con una regex, nonostante il costo aggiuntivo dell'aggiunta di regex.
RegularExpressionsarenoworsetoreadormaintainthananyotherunformattedcontent; indeedaregexisprobablyeasiertoreadthanthispieceoftexthere-butunfortunatelytheyhaveabadreputationbecausesomeimplementationsdon'tallowformattingandpeopleingeneraldon'tknowthatyoucandoit.
(Le espressioni regolari non sono peggiori da leggere o mantenere rispetto a qualsiasi altro contenuto non formattato; in effetti una regex è probabilmente più facile da leggere rispetto a questo pezzo di testo qui - ma sfortunatamente hanno una cattiva reputazione perché alcune implementazioni non consentono la formattazione e le persone in generale non so che puoi farlo.)
Ecco un esempio banale:
^(?:[^,]*+,){21}[^,]*+$
Il che non è poi così difficile da leggere o mantenere, ma è ancora più facile quando appare così:
(?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
Questo è un esempio un po 'esagerato (commentare $
è simile al commentare i++
) ma chiaramente non dovrebbe esserci alcun problema a leggere, comprendere e mantenerlo.
Finché sei chiaro su quando sono adatte le espressioni regolari e quando sono una cattiva idea, non c'è nulla di sbagliato in esse, e la maggior parte delle volte la citazione JWZ non si applica davvero.
*+
? Come è diverso (funzionalmente) da giusto *
?
*+
in questo caso; tutto è ancorato e può essere abbinato in un singolo passaggio da un automa che può contare fino a 22. Il modificatore corretto su quei set non virgola è semplicemente vecchio *
. (Inoltre, non dovrebbero esserci differenze tra algoritmi di corrispondenza avidi e non avidi qui. È un caso estremamente semplice.)
Oltre alla risposta di ChrisF - che le espressioni regolari "sono difficili da programmare, comprendere e mantenere", c'è di peggio: sono abbastanza potenti da indurre le persone a tentare di usarle per analizzare cose che non possono, come l'HTML. Vedi le numerose domande su SO su "come posso analizzare HTML?" Ad esempio, la singola risposta più epica in tutto SO!
Le espressioni regolari sono molto potenti, ma hanno un piccolo e un grosso problema; sono difficili da scrivere e quasi impossibili da leggere.
Nel migliore dei casi l'uso dell'espressione regolare risolve il problema, quindi hai solo il problema di manutenzione del codice complicato. Se non ottieni l'espressione regolare nel modo giusto, hai sia il problema originale che il problema con il codice illeggibile che non funziona.
A volte le espressioni regolari vengono chiamate codice di sola scrittura. Di fronte a un'espressione regolare che deve essere riparata, spesso è più veloce ricominciare da capo che cercare di capire l'espressione.
Il problema è che regex è una bestia complicata e risolvi il problema solo se usi regex perfettamente. In caso contrario, si verificano 2 problemi: il problema originale e regex.
Sostieni che può fare il lavoro di cento righe di codice, ma potresti anche sostenere che 100 righe di codice chiaro e conciso sono meglio di una riga di regex.
Se hai bisogno di una prova di ciò: puoi dare un'occhiata a questo SO Classic o semplicemente pettinare il tag SO Regex
Il significato ha due parti:
Come lo chiedi nel 2014, sarebbe interessante concentrarsi sulle ideologie dei linguaggi di programmazione del contesto 1997 rispetto al contesto odierno. Non entrerò in questo dibattito qui, ma le opinioni su Perl e Perl sono cambiate notevolmente.
Tuttavia, per rimanere in un contesto del 2013 ( de l'eau a coulé sous les ponts depuis), suggerirei di concentrarmi sulla rievocazione tra virgolette usando un famoso fumetto XKCD che è una citazione diretta di quella di Jamie Zawinski :
Per prima cosa ho avuto problemi a capire questo fumetto perché era un riferimento alla citazione di Zawinski e una citazione di un testo di una canzone Jay-z e un riferimento a GNU program --help -z
flag 2 , quindi era troppo cultura per me capirlo.
Sapevo che era divertente, lo sentivo, ma non sapevo davvero perché. Le persone spesso fanno battute su Perl e regex, soprattutto perché non è il linguaggio di programmazione più alla moda, non sanno davvero perché dovrebbe essere divertente ... Forse perché i mongers di Perl fanno cose stupide .
Quindi la citazione iniziale sembra essere uno scherzo sarcastico basato su problemi di vita reale (dolore?) Causati dalla programmazione con strumenti che fanno male. Proprio come un martello può ferire un muratore, programmando con strumenti che non sono quelli che uno sviluppatore sceglierebbe se potesse ferire (il cervello, i sentimenti). A volte, si tengono grandi dibattiti su quale strumento sia il migliore, ma è quasi inutile perché è un problema di gusti o gusti del team di programmazione , ragioni culturali o economiche . Un altro eccellente fumetto XKCD su questo:
Riesco a capire che le persone provano dolore per le regex e credono che un altro strumento sia più adatto a ciò per cui sono progettate le regex. Poiché @ karl-bielefeldt risponde alla tua domanda con grande espressività, derivano grandi responsabilità , e le regex ne sono particolarmente preoccupate. Se uno sviluppatore non si preoccupa di come gestisce le regex, alla fine sarà un dolore per le persone che manterranno il codice in seguito.
Concluderò con questa risposta sulla rievocazione delle citazioni con una citazione che mostra un tipico esempio delle migliori pratiche del Perl di Damian Conw (un libro del 2005).
Spiega che scrivere uno schema come questo:
m{'[^\\']*(?:\\.[^\\']*)*'}
... non è più accettabile della scrittura di un programma come questo :
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<>;
Ma può essere riscritto , non è ancora carino, ma almeno ora è sopravvissibile.
# 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
Questo tipo di codice di forma rettangolare è il secondo problema, non regexes che può essere formattato in modo chiaro, mantenibile e leggibile.
/* 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 */ }
Se c'è una cosa che dovresti imparare dall'informatica, è la gerarchia di Chomsky . Direi che tutti i problemi con le espressioni regolari provengono da tentativi di analisi della grammatica senza contesto. Quando puoi imporre un limite (o pensare di poter imporre un limite) ai livelli di nidificazione in CFG, ottieni quelle espressioni regolari lunghe e complesse.
Le espressioni regolari sono più adatte per la tokenizzazione che per l'analisi su vasta scala.
Ma una serie sorprendentemente ampia di cose che i programmatori devono analizzare sono analizzabili con un linguaggio normale (o, peggio, quasi analizzabili con un linguaggio normale e se scrivi solo un po 'più di codice ...).
Quindi, se uno è abituato a "aha, ho bisogno di separare il testo, userò un'espressione regolare", è facile percorrere quella strada, quando hai bisogno di qualcosa che è più vicino a un automa push-down, un parser CFG o grammatiche ancora più potenti. Questo di solito finisce in lacrime.
Quindi, penso che la citazione non sia così tanto regexps che sbattono, hanno il loro uso (e ben usati, sono davvero molto utili), ma l'eccessiva dipendenza dai regexps (o, in particolare, dalla loro scelta acritica) .
jwz è semplicemente fuori di testa con quella citazione. le espressioni regolari non sono diverse da qualsiasi caratteristica del linguaggio: facile da rovinare, difficile da usare elegantemente, a volte potente, a volte inappropriato, spesso ben documentato, spesso utile.
lo stesso si può dire per l'aritmetica in virgola mobile, le chiusure, l'orientamento agli oggetti, l'I / O asincrono o qualsiasi altra cosa si possa nominare. se non sai cosa stai facendo, i linguaggi di programmazione possono renderti triste.
se ritieni che le regex siano difficili da leggere, prova a leggere l'implementazione del parser equivalente per utilizzare il modello in questione. spesso le regex vincono perché sono più compatte dei parser completi ... e nella maggior parte delle lingue sono anche più veloci.
non essere scoraggiato dall'uso di espressioni regolari (o di qualsiasi altra funzione linguistica) perché un blogger autopromozione fa dichiarazioni non qualificate. prova le cose per te e vedi cosa funziona per te.
La mia risposta preferita e approfondita a questo è data dal famoso Rob Pike in un post sul blog riprodotto da un commento sul codice Google interno: http://commandcenter.blogspot.ch/2011/08/regular-expressions-in-lexing- and.html
Il riassunto è che non è che sono cattivi , ma sono spesso usati per compiti per i quali non sono necessariamente adatti, specialmente quando si tratta di lessing e analisi di alcuni input.
Le espressioni regolari sono difficili da scrivere, difficili da scrivere bene e possono essere costose rispetto ad altre tecnologie ... D'altra parte, i Lexer sono abbastanza facili da scrivere correttamente (se non altrettanto compatto) e molto facili da testare. Prendi in considerazione la ricerca di identificatori alfanumerici. Non è troppo difficile scrivere il regexp (qualcosa come "[a-ZA-Z _] [a-ZA-Z_0-9] *"), ma in realtà non è molto più difficile scrivere come un semplice ciclo. Le prestazioni del loop, tuttavia, saranno molto più elevate e comporteranno molto meno codice sotto le copertine. Una libreria di espressioni regolari è una cosa importante. Usare uno per analizzare gli identificatori è come usare una Ferrari per andare al negozio per il latte.
Dice molto di più, sostenendo che le espressioni regolari sono utili in, ad esempio la corrispondenza dei pattern usa e getta negli editor di testo, ma dovrebbero essere usati raramente nel codice compilato e così via. Vale la pena leggere.
Questo è legato all'epigramma # 34 di Alan Perlis:
La stringa è una struttura di dati rigida e ovunque viene passata c'è molta duplicazione del processo. È un veicolo perfetto per nascondere informazioni.
Quindi, se si sceglie la stringa di caratteri come struttura dei dati (e, naturalmente, il codice basato su regex come algoritmi per manipolarlo), si ha un problema, anche se funziona: cattiva progettazione attorno a una rappresentazione inappropriata di dati che è difficile da esteso e inefficiente.
Tuttavia, spesso non funziona: il problema originale non viene risolto, quindi in questo caso hai due problemi.
I regex sono ampiamente utilizzati per l'analisi del testo rapida e sporca. Sono un ottimo strumento per esprimere schemi leggermente più complessi di una semplice corrispondenza di stringhe.
Tuttavia, man mano che le regex diventano più complesse, i problemi del server alzano la testa.
Quindi è fin troppo facile iniziare con un problema di elaborazione del testo, applicare espressioni regolari ad esso e finire con due problemi, il problema originale che stavi cercando di risolvere e gestire le espressioni regolari che stai tentando di risolvere (ma non risolvendo correttamente) il problema originale.