Gibt es einen bestimmten Grund für die schlechte Lesbarkeit des Syntaxentwurfs für reguläre Ausdrücke?

160

Alle Programmierer scheinen sich einig zu sein, dass die Lesbarkeit von Code weitaus wichtiger ist als einzeilige Zeichen mit kurzer Syntax, bei denen jedoch ein erfahrener Entwickler die Interpretation mit einem gewissen Maß an Genauigkeit durchführen muss - dies scheint jedoch genau die Art und Weise zu sein, wie reguläre Ausdrücke entworfen wurden. Gab es einen Grund dafür?

Wir sind uns alle einig, dass dies selfDocumentingMethodName()weitaus besser ist als e(). Warum sollte das nicht auch für reguläre Ausdrücke gelten?

Es scheint mir, dass anstatt eine Syntax von einzeiliger Logik ohne strukturelle Organisation zu entwerfen:

var parse_url = /^(?:([A-Za-z]+):)?(\/{0,3})(0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;

Und das ist nicht einmal das strikte Parsen einer URL!

Stattdessen könnten wir eine Pipeline-Struktur strukturiert und lesbar machen, um ein einfaches Beispiel zu geben:

string.regex
   .isRange('A-Z' || 'a-z')
   .followedBy('/r');

Welchen Vorteil bietet die extrem knappe Syntax eines regulären Ausdrucks neben der kürzestmöglichen Operations- und Logiksyntax? Gibt es letztendlich einen bestimmten technischen Grund für die schlechte Lesbarkeit des Syntaxentwurfs für reguläre Ausdrücke?

Viziionary
quelle
Kommentare sind nicht für eine längere Diskussion gedacht. Diese Unterhaltung wurde in den Chat verschoben .
maple_shaft
1
Ich habe versucht, genau dieses Lesbarkeitsproblem mit einer Bibliothek namens RegexToolbox anzugehen. Bisher wurde es auf C #, Java und JavaScript portiert - siehe github.com/markwhitaker/RegexToolbox.CSharp .
Mark Whitaker
Es wurden viele Versuche unternommen, um dieses Problem zu lösen, aber die Kultur ist schwer zu ändern. Siehe meine Antwort über verbale Ausdrücke hier . Die Menschen greifen nach dem niedrigsten verfügbaren Werkzeug.
Parivar Saraff

Antworten:

178

Es gibt einen wichtigen Grund, warum reguläre Ausdrücke so knapp gehalten wurden: Sie sollten als Befehle für einen Code-Editor und nicht als Sprache zum Codieren verwendet werden. Genauer gesagt, edwar eines der ersten Programme, die reguläre Ausdrücke verwendeten und von dort begannen reguläre Ausdrücke ihre Eroberung der Weltherrschaft. Der edBefehl g/<regular expression>/pinspirierte beispielsweise bald ein separates Programm mit dem Namen grep, das noch heute verwendet wird. Aufgrund ihrer Leistungsfähigkeit wurden sie anschließend standardisiert und in einer Vielzahl von Werkzeugen wie sedund eingesetztvim

Aber genug für die Kleinigkeiten. Warum sollte dieser Ursprung eine knappe Grammatik bevorzugen? Weil Sie keinen Editor-Befehl eingeben, um ihn noch einmal zu lesen. Es reicht aus, dass Sie sich daran erinnern können, wie man es zusammensetzt, und dass Sie das Zeug damit machen können, das Sie tun möchten. Jedes Zeichen, das Sie eingeben müssen, verlangsamt jedoch Ihren Fortschritt beim Bearbeiten Ihrer Datei. Die Syntax für reguläre Ausdrücke wurde entwickelt, um relativ komplexe Suchvorgänge wegwerfbar zu machen, und genau das bereitet denjenigen Kopfschmerzen, die sie als Code verwenden, um Eingaben in ein Programm zu analysieren.

cmaster
quelle
5
Regex soll nicht analysieren. ansonsten stackoverflow.com/questions/1732348/… . und Kopfschmerzen.
njzk2
19
@ njzk2 Diese Antwort ist eigentlich falsch. Ein HTML- Dokument ist keine reguläre Sprache, sondern ein offenes HTML- Tag , nach dem sich die Frage eigentlich richtet.
Random832,
11
Dies ist eine gute Antwort, die erklärt, warum regulärer Ausdruck so kryptisch ist wie er ist, aber nicht erklärt, warum es derzeit keinen alternativen Standard mit verbesserter Lesbarkeit gibt.
Doc Brown
13
Also für diejenigen, die denken, dass dies grepein falsch ausgesprochenes "Greifen" ist, kommt es tatsächlich von g/ re(für regulären Ausdruck) / p?
Hagen von Eitzen
6
@DannyPflughoeft Nein, das tut es nicht. Ein offenes Tag ist einfach <aaa bbb="ccc" ddd='eee'>, es sind keine Tags darin verschachtelt. Sie können keine Tags verschachteln. Was Sie verschachteln, sind Elemente (offenes Tag, Inhalte einschließlich untergeordneter Elemente, geschlossenes Tag), die die Frage nicht zum Parsen gestellt hat. HTML- Tags sind eine reguläre Sprache - das Ausgleichen / Verschachteln erfolgt auf einer Ebene oberhalb von Tags.
Random832
62

Der reguläre Ausdruck, den Sie zitieren, ist ein schreckliches Durcheinander, und ich glaube nicht, dass irgendjemand zustimmt, dass er lesbar ist. Gleichzeitig hängt ein Großteil dieser Hässlichkeit mit dem zu lösenden Problem zusammen: Es gibt mehrere Ebenen der Verschachtelung und die URL-Grammatik ist relativ kompliziert (sicherlich zu kompliziert, um in jeder Sprache prägnant zu kommunizieren). Es ist jedoch sicher richtig, dass es bessere Möglichkeiten gibt, um zu beschreiben, was dieser reguläre Ausdruck beschreibt. Warum werden sie nicht verwendet?

Ein großer Grund ist Trägheit und Allgegenwart. Es erklärt nicht, warum sie so populär wurden, aber jetzt, da sie es sind, kann jeder, der reguläre Ausdrücke kennt, diese Fähigkeiten (mit sehr wenigen Unterschieden zwischen Dialekten) in hundert verschiedenen Sprachen und tausend zusätzlichen Softwaretools anwenden ( zB Texteditoren und Kommandozeilen-Tools). Letztere würden und könnten übrigens keine Lösung verwenden, die dem Schreiben von Programmen gleichkommt , da sie von Nicht-Programmierern häufig verwendet werden.

Trotzdem werden reguläre Ausdrücke häufig überbeansprucht, das heißt, auch wenn ein anderes Tool viel besser wäre. Ich halte die Regex-Syntax nicht für schrecklich . Aber bei kurzen und einfachen Mustern ist es deutlich besser: Das archetypische Beispiel für Bezeichner in C-ähnlichen Sprachen [a-zA-Z_][a-zA-Z0-9_]*kann mit einem absoluten Minimum an Regex-Kenntnissen gelesen werden, und sobald dieser Balken erreicht ist, ist es sowohl offensichtlich als auch gut prägnant. Es ist nicht von Natur aus schlecht, weniger Zeichen zu benötigen, ganz im Gegenteil. Prägnanz ist eine Tugend, sofern Sie nachvollziehbar bleiben.

Es gibt mindestens zwei Gründe, warum sich diese Syntax bei einfachen Mustern wie diesen auszeichnet: Die meisten Zeichen müssen nicht mit einem Escapezeichen versehen werden, daher wird sie relativ natürlich gelesen, und es werden alle verfügbaren Interpunktionszeichen verwendet, um eine Vielzahl einfacher Parsing-Kombinatoren auszudrücken. Vielleicht am wichtigsten ist , erfordert es keine alles überhaupt für die Sequenzierung. Sie schreiben das erste, dann das, was danach kommt. Vergleichen Sie dies mit Ihrem followedBy, insbesondere wenn das folgende Muster kein wörtlicher, sondern ein komplizierterer Ausdruck ist.

Warum scheitern sie in komplizierteren Fällen? Ich sehe drei Hauptprobleme:

  1. Es gibt keine Abstraktionsmöglichkeiten. Formale Grammatiken, die aus demselben Gebiet der theoretischen Informatik stammen wie Regexes, haben eine Reihe von Produktionen, so dass sie Zwischenteilen des Musters Namen geben können:

    # 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+ ':'
    ...
    
  2. Wie wir oben sehen konnten, ist ein Leerzeichen ohne besondere Bedeutung nützlich, um eine augenschonende Formatierung zu ermöglichen. Gleiches gilt für Kommentare. Reguläre Ausdrücke können das nicht, weil ein Leerzeichen genau das ist, ein Literal ' '. Beachten Sie jedoch, dass einige Implementierungen einen "ausführlichen" Modus zulassen, in dem Leerzeichen ignoriert und Kommentare möglich sind.

  3. Es gibt keine Metasprache, um gängige Muster und Kombinatoren zu beschreiben. Zum Beispiel kann man eine digitRegel einmal schreiben und in einer kontextfreien Grammatik weiterverwenden, aber man kann nicht sozusagen eine "Funktion" definieren, die einer Produktion gegeben ist pund eine neue Produktion erzeugt, die etwas Besonderes damit macht, zum Beispiel erstellen eine Produktion für eine kommagetrennte Liste von Vorkommen von p.

Der von Ihnen vorgeschlagene Ansatz löst diese Probleme mit Sicherheit. Es löst sie einfach nicht sehr gut, weil es weitaus prägnanter handelt als nötig. Die ersten beiden Probleme können gelöst werden, während eine relativ einfache und knappe domänenspezifische Sprache verwendet wird. Das dritte, na ja ... eine programmatische Lösung erfordert natürlich eine universelle Programmiersprache, aber meiner Erfahrung nach ist das dritte bei weitem das geringste dieser Probleme. Nur wenige Muster haben genug Vorkommen für dieselbe komplexe Aufgabe, nach der sich der Programmierer nach der Möglichkeit sehnt, neue Kombinatoren zu definieren. Und wenn dies notwendig ist, ist die Sprache oft so kompliziert, dass sie ohnehin nicht mit regulären Ausdrücken analysiert werden kann und sollte.

Für diese Fälle gibt es Lösungen. Es gibt ungefähr zehntausend Parser-Combinator-Bibliotheken, die in etwa das tun, was Sie vorschlagen, nur mit einer anderen Menge von Operationen, häufig einer anderen Syntax und fast immer mit mehr Parsing-Power als reguläre Ausdrücke (dh sie befassen sich mit kontextfreien Sprachen oder einigen beträchtlichen Sprachen) Teilmenge davon). Dann gibt es Parser-Generatoren, die mit dem oben beschriebenen Ansatz "Verwenden Sie ein besseres DSL" gehen. Und es gibt immer die Möglichkeit, einen Teil des Parsings von Hand in richtigen Code zu schreiben. Sie können sogar mischen und abgleichen, indem Sie reguläre Ausdrücke für einfache Unteraufgaben verwenden und die komplizierten Dinge im Code ausführen, die die regulären Ausdrücke aufrufen.

Ich weiß nicht genug über die frühen Jahre des Rechnens, um zu erklären, wie reguläre Ausdrücke so populär wurden. Aber sie sind hier, um zu bleiben. Sie müssen sie nur mit Bedacht einsetzen und nicht , wenn das klüger ist.

Tulains Córdova
quelle
9
I don't know enough about the early years of computing to explain how regular expressions came to be so popular.Wir können jedoch eine Vermutung wagen: Eine einfache reguläre Ausdrucksmaschine ist sehr einfach zu implementieren, viel einfacher als ein effizienter kontextfreier Parser.
biziclop
15
@biziclop Ich würde diese Variable nicht überschätzen. Yacc, das anscheinend genug Vorgänger hatte, um als " noch ein weiterer Compiler-Compiler" bezeichnet zu werden, wurde in den frühen 70er Jahren erstellt und war in einer früheren Unix-Version enthalten grep(Version 3 vs. Version 4). Es scheint, dass der erste größere Einsatz von Regex im Jahr 1968 erfolgte.
Ich kann nur weiter machen, was ich auf Wikipedia gefunden habe (also würde ich es nicht zu 100% glauben), aber demnach yaccwurde 1975 die ganze Idee von LALR-Parsern (die zu der ersten Klasse von praktisch verwendbaren Parsern gehörten) entwickelt Während die erste Implementierung einer regulären Ausdrücke-Engine, die JIT kompilierte (!), im Jahr 1968 veröffentlicht wurde. Aber Sie haben Recht, es ist schwer zu sagen, was es geschwungen hat aus". Aber ich würde vermuten, sobald sie in Texteditoren eingesetzt wurden, wollten Entwickler sie auch in ihrer eigenen Software verwenden.
Biziclop
1
@ jpmc26 öffne sein Buch, JavaScript The Good Parts to the Regex Chapter.
Viziionary,
2
with very few differences between dialectsIch würde nicht sagen, dass es "sehr wenige" sind. Jede vordefinierte Zeichenklasse hat mehrere Definitionen zwischen verschiedenen Dialekten. Und es gibt auch Parsing-Macken, die für jeden Dialekt spezifisch sind.
nhahtdh
39

Historische Perspektive

Der Wikipedia-Artikel ist ziemlich detailliert über die Ursprünge regulärer Ausdrücke (Kleene, 1956). Die ursprüngliche Syntax war relativ einfach mit nur *, +, ?, |und Gruppierung (...). Es war knapp ( und lesbar, die beiden sind nicht unbedingt gegensätzlich), weil formale Sprachen dazu neigen, mit knappen mathematischen Notationen ausgedrückt zu werden.

Später entwickelten sich die Syntax und die Funktionen mit den Editoren weiter und wuchsen mit Perl , das sich bemühte, vom Design her knapp zu halten ( "allgemeine Konstruktionen sollten kurz sein" ). Dies hat die Syntax stark verkompliziert. Beachten Sie jedoch, dass die Benutzer jetzt an reguläre Ausdrücke gewöhnt sind und sie gut schreiben (wenn sie sie nicht lesen) können. Die Tatsache, dass sie manchmal nur zum Schreiben bestimmt sind, deutet darauf hin, dass sie im Allgemeinen nicht das richtige Werkzeug sind, wenn sie zu lang sind. Reguläre Ausdrücke sind bei Missbrauch in der Regel nicht lesbar.

Jenseits von stringbasierten regulären Ausdrücken

Wenn wir über alternative Syntaxen sprechen, schauen wir uns eine an, die bereits existiert ( cl-ppcre in Common Lisp ). Ihr langer regulärer Ausdruck kann ppcre:parse-stringwie folgt analysiert werden:

(let ((*print-case* :downcase)
      (*print-right-margin* 50))
  (pprint
   (ppcre:parse-string "^(?:([A-Za-z]+):)?(\\/{0,3})(0-9.\\-A-Za-z]+)(?::(\\d+))?(?:\\/([^?#]*))?(?:\\?([^#]*))?(?:#(.*))?$")))

... und ergibt folgende Form:

(:sequence :start-anchor
 (:greedy-repetition 0 1
  (:group
   (:sequence
    (:register
     (:greedy-repetition 1 nil
      (:char-class (:range #\A #\Z)
       (:range #\a #\z))))
    #\:)))
 (:register (:greedy-repetition 0 3 #\/))
 (:register
  (:sequence "0-9" :everything "-A-Za-z"
   (:greedy-repetition 1 nil #\])))
 (:greedy-repetition 0 1
  (:group
   (:sequence #\:
    (:register
     (:greedy-repetition 1 nil :digit-class)))))
 (:greedy-repetition 0 1
  (:group
   (:sequence #\/
    (:register
     (:greedy-repetition 0 nil
      (:inverted-char-class #\? #\#))))))
 (:greedy-repetition 0 1
  (:group
   (:sequence #\?
    (:register
     (:greedy-repetition 0 nil
      (:inverted-char-class #\#))))))
 (:greedy-repetition 0 1
  (:group
   (:sequence #\#
    (:register
     (:greedy-repetition 0 nil :everything)))))
 :end-anchor)

Diese Syntax ist ausführlicher, und wenn Sie sich die folgenden Kommentare ansehen, sind sie nicht unbedingt besser lesbar. Gehen Sie also nicht davon aus, dass die Dinge automatisch klarer werden, weil Sie eine weniger kompakte Syntax haben .

Wenn Sie jedoch Probleme mit Ihren regulären Ausdrücken haben, können Sie Ihren Code möglicherweise entschlüsseln und debuggen, indem Sie sie in dieses Format umwandeln. Dies ist ein Vorteil gegenüber auf Zeichenfolgen basierenden Formaten, bei denen es schwierig sein kann, einen einzelnen Zeichenfehler zu erkennen. Der Hauptvorteil dieser Syntax besteht darin, reguläre Ausdrücke mithilfe eines strukturierten Formats anstelle einer Zeichenfolgencodierung zu bearbeiten. Auf diese Weise können Sie solche Ausdrücke wie jede andere Datenstruktur in Ihrem Programm komponieren und erstellen . Wenn ich die obige Syntax verwende, liegt das im Allgemeinen daran, dass ich Ausdrücke aus kleineren Teilen erstellen möchte (siehe auch meine CodeGolf-Antwort ). Für Ihr Beispiel schreiben wir 1 :

`(:sequence
   :start-anchor
   ,(protocol)
   ,(slashes)
   ,(domain)
   ,(top-level-domain) ... )

Zeichenfolgenbasierte reguläre Ausdrücke können auch mithilfe von Zeichenfolgenverkettung und / oder Interpolation in Hilfsfunktionen erstellt werden. Es gibt jedoch Einschränkungen mit String - Manipulationen , die zu neigen Krempel den Code (man denke über Verschachtelung Probleme, nicht anders als Backticks vs. $(...)in bash, auch Escape - Zeichen Sie Kopfschmerzen geben kann).

Beachten Sie auch, dass das obige Formular Formulare zulässt, (:regex "string")damit Sie knappe Notationen mit Bäumen mischen können. All dies führt meiner Meinung nach zu einer guten Lesbarkeit und Kompositionsfähigkeit. es befasst sich mit den drei Problemen, die durch Delnan indirekt (dh nicht in der Sprache der regulären Ausdrücke selbst) ausgedrückt werden .

Schlussfolgern

  • Für die meisten Zwecke ist die knappe Notation tatsächlich lesbar. Es gibt Schwierigkeiten beim Umgang mit erweiterten Notationen, die ein Zurückverfolgen usw. beinhalten, aber ihre Verwendung ist selten gerechtfertigt. Die unberechtigte Verwendung regulärer Ausdrücke kann zu unlesbaren Ausdrücken führen.

  • Reguläre Ausdrücke müssen nicht als Zeichenfolgen codiert werden. Wenn Sie über eine Bibliothek oder ein Tool verfügen, mit dem Sie reguläre Ausdrücke erstellen und erstellen können, vermeiden Sie viele potenzielle Fehler im Zusammenhang mit Zeichenfolgenmanipulationen.

  • Alternativ sind formale Grammatiken besser lesbar und können Unterausdrücke besser benennen und abstrahieren. Terminals werden im Allgemeinen als einfache reguläre Ausdrücke ausgedrückt.


1. Möglicherweise möchten Sie Ihre Ausdrücke lieber zum Zeitpunkt des Lesens erstellen, da reguläre Ausdrücke in der Regel Konstanten in einer Anwendung sind. Siehe create-scannerund load-time-value:

'(:sequence :start-anchor #.(protocol) #.(slashes) ... )
Core-Dump
quelle
5
Vielleicht bin ich nur an die traditionelle RegEx-Syntax gewöhnt, aber ich bin mir nicht sicher, ob 22 etwas lesbare Zeilen einfacher zu verstehen sind als der entsprechende einzeilige reguläre Ausdruck.
3
@ dan1111 "etwas lesbar" ;-) Okay, aber wenn Sie einen wirklich langen regulären Ausdruck benötigen, ist es sinnvoll, Teilmengen wie " digits," zu definieren identund sie zu komponieren. Ich betrachte dies im Allgemeinen als Zeichenfolgenmanipulation (Verkettung oder Interpolation), die andere Probleme mit sich bringt, wie z. Suchen Sie beispielsweise nach Vorkommen \\\\`in emacs-Paketen. Übrigens ist dies noch schlimmer, da das gleiche Escape-Zeichen sowohl für Sonderzeichen wie \nund \"als auch für die Regex-Syntax verwendet wird \(. Ein Non-Lisp-Beispiel für gute Syntax ist printf, wo %dnicht widerspricht \d.
Coredump
1
fairer Punkt über die definierten Teilmengen. Das macht sehr viel Sinn. Ich bin nur skeptisch, dass die Ausführlichkeit eine Verbesserung darstellt. Für Anfänger mag es einfacher sein (obwohl Konzepte wie greedy-repetitionnicht intuitiv sind und erst noch erlernt werden müssen). Dies beeinträchtigt jedoch die Benutzerfreundlichkeit für Experten, da es viel schwieriger ist, das gesamte Muster zu erkennen und zu erfassen.
@ dan1111 Ich stimme zu, dass Ausführlichkeit für sich genommen keine Verbesserung darstellt. Was eine Verbesserung sein kann, ist die Manipulation von Regex mithilfe strukturierter Daten anstelle von Zeichenfolgen.
Coredump
@ dan1111 Vielleicht sollte ich eine Bearbeitung mit Haskell vorschlagen? Parsec macht es in nur neun Zeilen; als one-liner: do {optional (many1 (letter) >> char ':'); choice (map string ["///","//","/",""]); many1 (oneOf "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-."); optional (char ':' >> many1 digit); optional (char '/' >> many (noneOf "?#")); optional (char '?' >> many (noneOf "#")); optional (char '#' >> many (noneOf "\n")); eof}. Mit ein paar Zeilen wie der Bezeichnung der langen Zeichenfolge als domainChars = ...und section start p = optional (char start >> many p)es sieht ziemlich einfach aus.
CR Drost
25

Das größte Problem bei Regex ist nicht die zu knappe Syntax, sondern der Versuch, eine komplexe Definition in einem einzelnen Ausdruck auszudrücken, anstatt sie aus kleineren Bausteinen zusammenzusetzen. Dies ähnelt der Programmierung, bei der Sie niemals Variablen und Funktionen verwenden und stattdessen Ihren Code in eine einzige Zeile einbetten.

Vergleichen Sie Regex mit BNF . Die Syntax ist nicht viel sauberer als bei Regex, wird aber anders verwendet. Sie definieren zunächst einfach benannte Symbole und setzen sie zusammen, bis Sie zu einem Symbol kommen, das das gesamte Muster beschreibt, mit dem Sie übereinstimmen möchten.

Schauen Sie sich zum Beispiel die URI-Syntax in rfc3986 an :

URI           = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
scheme        = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
hier-part     = "//" authority path-abempty
              / path-absolute
              / path-rootless
              / path-empty
...

Mit einer Variante der Regex-Syntax, die das Einbetten benannter Unterausdrücke unterstützt, können Sie fast dasselbe schreiben.


Persönlich denke ich, dass eine knappe Regex-ähnliche Syntax für häufig verwendete Features wie Zeichenklassen, Verkettung, Auswahl oder Wiederholung in Ordnung ist, aber für komplexere und seltenere Features wie ausführliche Look-Ahead-Namen sind sie vorzuziehen. Ganz ähnlich, wie wir Operatoren wie +oder *in der normalen Programmierung verwenden und für seltenere Operationen auf benannte Funktionen umschalten.

CodesInChaos
quelle
12

selfDocumentingMethodName () ist weitaus besser als e ()

ist es? Es gibt einen Grund, warum die meisten Sprachen {und} als Blocktrennzeichen haben und nicht BEGIN und END.

Leute mögen Knappheit, und wenn Sie die Syntax kennen, ist eine kurze Terminologie besser. Stellen Sie sich Ihr Regex-Beispiel vor, wenn d (für digit) 'digit' wäre, wäre der Regex noch entsetzlicher zu lesen. Wenn Sie die Syntaxanalyse mit Steuerzeichen vereinfachen würden, würde dies eher wie XML aussehen. Weder sind so gut, wenn Sie die Syntax kennen.

Um Ihre Frage richtig zu beantworten, müssen Sie jedoch wissen, dass Regex aus der Zeit stammt, in der Kürze vorgeschrieben war. Es ist leicht zu glauben, dass ein 1-MB-XML-Dokument heutzutage keine große Sache ist, aber wir sprechen von Tagen, in denen 1 MB ziemlich viel war Ihre gesamte Speicherkapazität. Damals wurden auch weniger Sprachen verwendet, und Regex ist keine Million Meilen von Perl oder C entfernt, sodass die Syntax den damaligen Programmierern vertraut wäre, die mit dem Erlernen der Syntax zufrieden wären. Es gab also keinen Grund, es ausführlicher zu gestalten.

gbjbaanb
quelle
1
selfDocumentingMethodNameMan ist sich im Allgemeinen einig , dass es besser ist, als eweil die Intuition des Programmierers nicht mit der Realität übereinstimmt, was eigentlich Lesbarkeit oder guten Code ausmacht . Die Leute, die zustimmen, sind falsch, aber so ist es.
Leushenko
1
@Leushenko: Behauptest du, das e()ist besser als selfDocumentingMethodName()?
JacquesB
3
@JacquesB möglicherweise nicht in allen Zusammenhängen (wie ein globaler Name). Aber für eng begrenzte Dinge? Fast sicher. Auf jeden Fall öfter als die konventionelle Weisheit sagt.
Leushenko
1
@Leushenko: Es fällt mir schwer, mir einen Kontext vorzustellen, in dem ein einzelner Funktionsname besser ist als ein aussagekräftigerer Name. Aber ich denke, das ist reine Meinung.
JacquesB
1
@MilesRout: Das Beispiel bezieht sich eigentlich auf e()einen selbstdokumentierenden Methodennamen . Können Sie erklären, in welchem ​​Kontext es eine Verbesserung ist, Methodennamen mit einem Buchstaben anstelle von beschreibenden Methodennamen zu verwenden?
JacquesB
6

Regex ist wie Legostücke. Auf den ersten Blick sieht man einige unterschiedlich geformte Kunststoffteile, die verbunden werden können. Du denkst vielleicht, es gäbe nicht zu viele mögliche unterschiedliche Dinge, die du formen kannst, aber dann siehst du die erstaunlichen Dinge, die andere Leute tun, und du fragst dich nur, wie ein erstaunliches Spielzeug es ist.

Regex ist wie Legostücke. Es gibt nur wenige Argumente, die verwendet werden können, aber wenn Sie sie in verschiedenen Formen verketten, entstehen Millionen verschiedener Regex-Muster, die für viele komplizierte Aufgaben verwendet werden können.

Menschen verwendeten selten Regex-Parameter allein. In vielen Sprachen können Sie die Länge eines Strings überprüfen oder die numerischen Teile daraus abtrennen. Sie können Zeichenfolgenfunktionen verwenden, um Texte aufzuteilen und zu reformieren. Die Leistungsfähigkeit von Regex wird deutlich, wenn Sie komplexe Formulare verwenden, um sehr spezifische komplexe Aufgaben zu erledigen.

Sie können Zehntausende von Regex-Fragen auf SO finden und sie werden selten als Duplikat markiert. Dies allein zeigt die möglichen einzigartigen Anwendungsfälle, die sich stark voneinander unterscheiden.

Und es ist nicht einfach, vordefinierte Methoden anzubieten, um diese vielen verschiedenen einzigartigen Aufgaben zu bewältigen. Sie haben Zeichenfolgenfunktionen für diese Art von Aufgaben, aber wenn diese Funktionen für Ihre spezifische Aufgabe nicht ausreichen, ist es an der Zeit, reguläre Ausdrücke zu verwenden

Gefallener Engel
quelle
2

Ich erkenne, dass dies eher ein Übungsproblem als ein Potenzproblem ist. Das Problem tritt normalerweise auf, wenn reguläre Ausdrücke direkt implementiert werden, anstatt eine zusammengesetzte Natur anzunehmen. Ebenso wird ein guter Programmierer die Funktionen seines Programms in prägnante Methoden zerlegen.

Beispielsweise könnte eine reguläre Zeichenfolge für eine URL von ungefähr reduziert werden:

UriRe = [scheme][hier-part][query][fragment]

zu:

UriRe = UriSchemeRe + UriHierRe + "(/?|/" + UriQueryRe + UriFragRe + ")"
UriSchemeRe = [scheme]
UriHierRe = [hier-part]
UriQueryRe = [query]
UriFragRe = [fragment]

Reguläre Ausdrücke sind nette Dinge, aber sie neigen dazu, von denen missbraucht zu werden, die sich in ihrer scheinbaren Komplexität vertiefen. Die resultierenden Ausdrücke sind rhetorisch, ohne einen langfristigen Wert.

toplel32
quelle
2
Leider enthalten die meisten Programmiersprachen keine Funktionen, die beim Erstellen von Regexen hilfreich sind, und die Funktionsweise der Gruppenerfassung ist auch nicht sehr kompositionsfreundlich.
CodesInChaos
1
Andere Sprachen müssen Perl 5 in ihrer Unterstützung für "Perl-kompatible reguläre Ausdrücke" nachholen. Unterausdrücke sind nicht dasselbe wie das einfache Verketten von Zeichenfolgen mit regulären Ausdrücken. Captures sollten benannt werden und sich nicht auf die implizite Nummerierung stützen.
JDługosz,
0

Wie @cmaster sagt, waren reguläre Ausdrücke ursprünglich so konzipiert, dass sie nur im laufenden Betrieb verwendet werden können, und es ist einfach bizarr (und leicht bedrückend), dass die Syntax für Zeilenrauschen immer noch die beliebteste ist. Die einzigen Erklärungen, die mir einfallen, sind entweder Trägheit, Masochismus oder Machismus (es kommt nicht oft vor, dass Trägheit der attraktivste Grund ist, etwas zu tun ...)

Perl unternimmt einen eher schwachen Versuch, sie lesbarer zu machen, indem es Leerzeichen und Kommentare zulässt, tut aber nichts, was man sich nur aus der Ferne vorstellen kann.

Es gibt andere Syntaxen. Eine gute ist die scsh-Syntax für reguläre Ausdrücke , die meiner Erfahrung nach reguläre Ausdrücke erzeugt, die einigermaßen einfach zu tippen sind, aber im Nachhinein noch lesbar sind.

[ scsh ist aus anderen Gründen großartig, nur einer davon ist der berühmte Anerkennungs-Text ]

Norman Gray
quelle
2
Perl6 macht es! Schauen Sie sich Grammatiken an.
JDługosz
@ JDługosz Soweit ich sehe, sieht das eher nach einem Mechanismus für Parsergeneratoren aus, als nach einer alternativen Syntax für reguläre Ausdrücke. Aber der Unterschied ist vielleicht nicht tiefgreifend.
Norman Gray
Es kann ein Ersatz sein, ist aber nicht auf die gleiche Leistung beschränkt. Sie könnten ein regedp in eine Inline-Grammatik mit 1: 1-Entsprechung der Modifikatoren übersetzen, jedoch in einer besser lesbaren Syntax. Beispiele für die Bewerbung als solche finden sich in der ursprünglichen Perl-Apokalypse.
JDługosz,
0

Ich glaube, reguläre Ausdrücke wurden so entworfen, dass sie so allgemein und einfach wie möglich sind, so dass sie (ungefähr) überall gleich verwendet werden können.

Ihr Beispiel regex.isRange(..).followedBy(..)ist sowohl an die Syntax einer bestimmten Programmiersprache als auch an den möglicherweise objektorientierten Stil (Methodenverkettung) gekoppelt.

Wie würde diese exakte "Regex" beispielsweise in C aussehen? Der Code müsste geändert werden.

Der allgemeinste Ansatz wäre die Definition einer einfachen prägnanten Sprache, die dann problemlos und ohne Änderung in eine andere Sprache eingebettet werden kann. Und das ist (fast) was Regex ist.

Aviv Cohn
quelle
0

Perl-kompatible reguläre Ausdrücke sind weit verbreitet und bieten eine kurze Syntax für reguläre Ausdrücke, die viele Editoren und Sprachen verstehen. Wie @ JDługosz in Kommentaren ausführte, hat Perl 6 (nicht nur eine neue Version von Perl 5, sondern eine völlig andere Sprache) versucht, reguläre Ausdrücke lesbarer zu machen, indem sie aus individuell definierten Elementen aufgebaut wurden. Hier ist zum Beispiel eine Beispielgrammatik zum Parsen von URLs aus Wikibooks :

grammar URL {
  rule TOP {
    <protocol>'://'<address>
  }
  token protocol {
    'http'|'https'|'ftp'|'file'
  }
  rule address {
    <subdomain>'.'<domain>'.'<tld>
  }
  ...
}

Wenn Sie den regulären Ausdruck wie folgt aufteilen, können Sie jedes Bit einzeln definieren (z. B. einschränken domain, um alphanumerisch zu sein) oder durch Unterklassen erweitern (z. B. nur FileURL is URLdiese einschränken, um protocolzu sein "file").

Also: Nein, es gibt keinen technischen Grund für die Schärfe regulärer Ausdrücke, aber neuere, übersichtlichere und besser lesbare Darstellungsweisen gibt es bereits! Hoffentlich sehen wir einige neue Ideen in diesem Bereich.

Gaurav
quelle