it-swarm.com.de

Sind reguläre Ausdrücke eine Programmiersprache?

Qualifizieren sich reguläre Ausdrücke im akademischen Sinne als Programmiersprache?

Die Motivation für meine Neugier ist eine SO Frage Ich habe mir gerade angesehen, welche Frage "Kann Regex X machen?" und ich fragte mich, was man im allgemeinen Sinne über die möglichen Lösungen sagen kann, die sie verwenden.

Ich frage grundsätzlich: "Sind reguläre Ausdrücke vollständig?"

28
Aaron Anodide

Reguläre Ausdrücke sind eine bestimmte Art von formale Grammatik , die zum Parsen von Zeichenfolgen und anderen Textinformationen verwendet wird, die in der formalen Sprachtheorie als "reguläre Sprachen" bezeichnet werden. Sie sind keine Programmiersprache als solche. Sie sind eher eine Abkürzung für die Codierung, deren Implementierung ansonsten äußerst mühsam und noch verwirrender wäre als der manchmal arkan aussehende Regex.

Programmiersprachen werden normalerweise als Sprachen definiert, die Turing Complete sind. Solche Sprachen müssen in der Lage sein, jede berechenbare Funktion zu verarbeiten. Regex passt nicht in diese Kategorie.

Wenn Sie eine Sprache möchten, die wie Regex aussieht, versuchen Sie es mit J.

49
World Engineer

Es ist schwierig, Fragen vom Typ "ist X a Y " zu beantworten, wenn Die Teilnehmer der Debatte verwenden unterschiedliche Definitionen von X und Y . Es könnte sein, dass für einige Definitionen die Antwort "Ja" und für einige Definitionen "Nein" lautet. Besonders wenn die Antwort von technische Details abhängt, wo sich die verschiedenen Definitionen unterscheiden. Auch diese Diskussion enthält einige Fehlinformationen. Bitte haben Sie etwas Geduld mit einer längeren Antwort.

Was meinen wir mit einer " Programmiersprache "?

Eine einfache Antwort könnte "eine Sprache sein, die zum Erstellen von Programmen verwendet wird". Klar, aber: welche Art von Programmen? Was ist mit einer Sprache, mit der einige Arten von Programmen erstellt werden können, aber keine anderen Arten von Programmen? Hier sind zwei spezifische Beispiele, um die Extremfälle zu veranschaulichen:

1) Eine imaginäre Sprache namens M funktioniert folgendermaßen: Wenn das Programm den einzelnen Buchstaben "m" enthält, wird ein Minesweeper-Spiel erstellt. Alles andere ist ein Syntaxfehler.

Intuitiv ist dies nicht ​​was wir unter "Programmiersprache" verstehen. Aber die Marketingabteilung von M könnte argumentieren, dass es technisch die Definition erfüllt, weil es kann verwendet werden, um ein Programm zu erstellen. Sicher, der Compiler erledigt einige wichtige Teile für Sie, aber genau das tun Compiler, nicht wahr? Ein Compiler der Sprache C übersetzt auch einige einfache Wörter in Dutzende von Prozessoranweisungen. Der M-Compiler geht noch weiter und macht Ihre Arbeit noch einfacher.

2) Wenn Sie die Originalversion des berühmten Turbo Pascal installieren, können Sie viele Arten von Programmen schreiben. Sie können jedoch kein Spiel schreiben, das im Webbrowser ausgeführt wird, da die erforderliche API einfach nicht vorhanden ist.

Was genau macht Turbo Pascal zu einer Programmiersprache, aber M hat sie nicht? Einfach gesagt, Sie können mehr in Pascal als in M ​​tun. Stellen Sie sich jedoch vor, wir haben ein M.NET, das ein Minesweeper-Spiel erstellt, das in einem Webbrowser ausgeführt wird. Jetzt haben wir also etwas, was Pascal kann und M.NET nicht kann, aber wir haben auch etwas, das M.NET kann und Pascal nicht kann. Warum sollten wir die Vorteile von Pascal für wichtig und die Vorteile von M.NET für irrelevant halten?

Die Antwort ist, dass Sie alle Arten von Algorithmen in Pascal schreiben können, aber Sie können nicht Algorithmen in M ​​oder M.NET schreiben. Sicher, M kompiliert Ihren Befehl "m" und C kompiliert Ihren Befehl "strcmp". Sie können "strcmp" jedoch in einen größeren Kontext stellen, z. B. zwei Dateien zeilenweise vergleichen oder tausend Zeichenfolgen lesen und alphabetisch sortieren oder ... nun ja, Millionen anderer Dinge. Und genau diese Fähigkeit, gegebene Befehle in einem beliebigen Algorithmus zu verwenden, macht das Wesentliche einer Programmiersprache aus.

Was genau ist ein Algorithmus und was noch wichtiger ist, was ist "irgendein Algorithmus"? In der Informatik verwenden wir die Wörter Turing-complete . Die Idee ist, dass es eine Reihe von Computersprachen gibt, in denen jede von ihnen simulieren alle von ihnen kann. Eine dieser Sprachen ist die Turing-Maschine, weshalb sie so genannt werden. Pascal ist da, C ist da, Java ist da, Python ist da, LISP ist da, Smalltalk ist da, sogar XSLT ist da. Unser hypothetisches M. und M.NET sind nicht ​​da. Sie können an jeder Universität, die einen anständigen Informatikkurs anbietet, mehr darüber erfahren, aber die Idee ist, dass eine Turing-vollständige Sprache alles kann = dass eine andere Turing-vollständige Sprache dies kann, wenn Sie ihnen die erforderliche Mindest-API geben. (Wenn Sie Pascal eine Webbrowser-API geben, können Sie alle Arten von Spielen im Webbrowser erstellen. Wenn Sie eine Webbrowser-API angeben Für M können Sie immer noch nur Minesweeper erstellen.) Wir könnten metaphorisch sagen, dass, wenn Sie alle APIs aus einer Programmiersprache entfernen, das Wichtige übrig bleibt.

Was meinen wir mit " regulären Ausdrücken "?

Verschiedene Programmiersprachen implementieren sie etwas unterschiedlich. Die ursprüngliche Idee war jedoch, dass reguläre Ausdrücke sogenannte reguläre Sprachen ausdrücken. Beachten Sie, dass wir hier nicht über Programmiersprachen sprechen, sondern über (pseudo-) menschliche Sprachen. Stellen Sie sich vor, Sie finden einen exotischen Stamm, der eine Sprache spricht, die nur aus den Wörtern "ba", "baba", "bababa" usw. besteht. Sie könnten diese Sprache verbal als "eine Silbe 'ba' beschreiben, die ein- oder mehrmals wiederholt wird" oder einen regulären Ausdruck als "(ba) +" verwenden.

Die regulären Ausdrücke sollen ausdrücken: "nichts", "dieser Buchstabe", "dies, gefolgt von dem", "dies oder das", "dies, ein- oder mehrmals wiederholt" und "nicht dies". - Das ist die mathematische Definition. Alles andere ist nur eine praktische Verknüpfung, die aus den vorherigen Komponenten erstellt wurde. Zum Beispiel kann "dies, zwei- oder dreimal wiederholt" als "dies, gefolgt von diesem, gefolgt von (dies oder nichts)" übersetzt werden, aber es könnte bequemer sein, "ba {2,3}" als "baba" zu schreiben (ba)? ".

Im wirklichen Leben implementiert eine typische Implementierung von "regulären Ausdrücken" mehr als dies. Verwenden Sie beispielsweise die mathematische Definition, eine Sprache von "aba", "aabaa", "aaabaaa" usw. - eine beliebige Anzahl von "a", gefolgt von einem "b", gefolgt von dasselbe Anzahl der "a" s - ist nicht ​​eine reguläre Sprache. Viele heute verwendete "reguläre Ausdrücke" könnten dies jedoch anhand des zusätzlichen Konzepts "dasselbe, was wir zuvor gefunden haben" erkennen, das als "(a +) b\1" geschrieben wurde. Mit diesem zusätzlichen Konzept können wir einige coole Dinge tun, zum Beispiel Wörter erkennen, die aus prime Anzahl der Buchstaben bestehen. Trotzdem können wir nicht irgendein Algorithmus ... für eine Erklärung, warum, studieren Sie bitte ein Lehrbuch über formale Sprachen .

Zurück zum ursprünglichen Thema: Sind reguläre Ausdrücke (definiert als: Ausdrücke, die reguläre Sprachen in der Chomsky-Hierarchie beschreiben, oder als: erstere plus die Operation\1) eine Programmiersprache (definiert als: Turing-complete)? Die Antwort lautet nein . Nein, Sie können jeden Algorithmus nicht mit regulären Ausdrücken implementieren, und die Fähigkeit, jeden Algorithmus zu implementieren, verstehen die Informatiker normalerweise als das Wesen der Programmiersprache.

Natürlich kann jeder die Antwort ändern, indem er auf einem andere Definition besteht. Wie ich am Anfang schrieb, sind hier die technischen Details wichtig. Wenn Sie sie falsch verstehen, erhalten Sie eine falsche Antwort.

Und wenn Sie nicht ​​an technischen Details interessiert sind, könnte die Antwort lauten: Können Sie reguläre Ausdrücke (und sonst nichts) verwenden, um ein Programm zu erstellen? Warum also Programmiersprache? (Eine solche Antwort wurde jedoch hier heruntergeladen und gelöscht, weshalb ich diese längere Version geschrieben habe.)

BEARBEITEN: Außerdem kann jeder eine Bibliothek erstellen, die seine eigene neue Variante von "regulären Ausdrücken" mit einigen zusätzlichen neuen Funktionen implementiert. Irgendwann reichen die neuen Funktionen möglicherweise aus, damit das gesamte System Turing-vollständig wird. Ein triviales Beispiel wäre das Einbetten einer Turing-vollständigen Sprache unter Verwendung einer neuen Syntax. es kann aber auch weniger offensichtlich passieren. Vielleicht ist es schon passiert.

14
Viliam Búr

Obwohl ein Suchen/Ersetzen in regulären Ausdrücken keine Turing-vollständige Programmiersprache ist, wie in den vorherigen Antworten erläutert, können Sie jede Turing-Maschine mit regulären Ausdrücken codieren, wenn Sie wiederholte Aktionen zum Ersetzen durch reguläre Ausdrücke zulassen.

Das wiederholte Suchen/Ersetzen durch reguläre Ausdrücke ist eine Turing-vollständige Programmiersprache

Infolgedessen können Sie jede berechenbare Funktion mit derselben Suche berechnen und den regulären Javascript-Ausdruck immer wieder ersetzen.

Um die Vollständigkeit der Turing zu beweisen, reicht es aus, eine Turing-Maschine in der Suche/Ersetzung durch reguläre Ausdrücke zu codieren. Angenommen, der Status des Editors lautet:

0000#12345:01-5:0#0000000

welches als Symbolband mit einem Lesegerät darauf gelesen werden kann:

[left symbols]#[set of states]:[set of symbols]-[current state]:[current symbol]#[right symbols]

Für die Regel, die 0 in Zustand 5 liest, 1 schreibt und ihren Zustand in 3 ändert und sich nach links bewegt, abstrahieren wir sie mit der folgenden Notation:

5:0 => 1, 3:[left]

Wir codieren die vorherige Notation in einen regulären Suchausdruck:

(\d)#(1)(2)(3)(4)(5):(0)(1)-5:0#

und sein Ersatzausdruck (Javascript-ähnlich)

#12345:01-$4:$1#$8

Ok, wie codiere ich jetzt viele Regeln? Wir verwenden die Verkettung mit dem Operator or|für die Suche nach regulären Ausdrücken, und wir kombinieren die Ergebnisse als Ersatz, indem wir Gruppennummern mit Offsets nummerieren. Betrachten wir zum Beispiel den Satz von vier Regeln.

5:0 => 1, 3:left
3:0 => 1, 5:right
5:1 => 1, 5:right
3:1 => 1: 3:stop

Wir codieren sie in einem Such- und Ersetzungsausdruck:

Search:
(\d)#(1)(2)(3)(4)(5):(0)(1)-5:0#|#(1)(2)(3)(4)(5):(0)(1)-3:0#(\d)|#(1)(2)(3)(4)(5):(0)(1)-5:1#(\d)|#(1)(2)(3)(4)(5):(0)(1)-3:1#

Replace by:
$15$23#12345:01-$4$13$21$27:$1$16$24$31#$8

Probieren Sie es in Ihrer Lieblings-Javascript-Engine aus:

function turingstep(s) {
  return s.replace(/(\d)#(1)(2)(3)(4)(5):(0)(1)-5:0#|#(1)(2)(3)(4)(5):(0)(1)-3:0#(\d)|#(1)(2)(3)(4)(5):(0)(1)-5:1#(\d)|#(1)(2)(3)(4)(5):(0)(1)-3:1#/g,"$15$23#12345:01-$4$13$21$27:$1$16$24$31#$8");
}

var tape = "0000#12345:01-5:0#0000000"
for(var i = 0; i < 6; i++) {
  console.log(tape)
  tape = turingstep(tape)
}
1
Mikaël Mayer

In .Net kann Regex nicht nur mehrere Arten von Bedingungen mit unterschiedlichen Kombinationen von Abwechslung und Lookarounds verarbeiten, sondern auch seinen eigenen Stapel bearbeiten.

(?xm)
    (?>
        <(?<Tagname>table)[^>]*>
    )
(?(Tagname)
    (
        </(?(?!\k'Tagname')(?<-Tagname>))*\k'Tagname'>(?<-Tagname>)
    |
        (?>
            <(?<Tagname>[a-z][^\s>]*)[^>]*>
        )
    |
        [^<]+
    )+?
    (?(Tagname)(?!))
)

Dies ist zum Beispiel ein kleiner Ausschnitt, den ich geschrieben habe, um eine HTML-Tabelle abzurufen. Im Gegensatz zu anderen Regex-Engines steuert dies den Stapel von Erfassungssammlungen (Push, Peek und Pop) und kann verschachtelte Objekte verarbeiten. Ich habe eine komplexere, aber sie ist irgendwie proprietär.

Ich denke, in diesem Beispiel kann Regex so angesehen werden, dass es alle grundlegenden Anforderungen einer Programmiersprache erfüllt. Es verfügt über Variablen, Inline-Speicher, Bedingungen, Eingabe und Ausgabe und wird mit einer von mehreren Regex-Kompilierungs-Engines (in diesem Fall .Net) kompiliert.

Als Antwort auf das überstrapazierte Kreischen, um (NIE) HTML mit Regex zu analysieren, habe ich eine vorab eingegebene Antwort veröffentlicht, die ich veröffentlichen kann: HTML analysieren

Ein weiteres Beispiel (nur eine Demonstration) ist das Folgende:

Function Regex("<(td>)((?:[^<]*(?(?!</\1)<))*)</\1")
    Group(0) = "<"
    Group(1) = "td>"
    Group(0) += Group(1)
    Group(2) = LoopMethod()
    Group(0) += Group(2)
    Group(0) += "</" & Group(1)
    Return Group()
End Function

Function LoopMethod()
    retGroup = ""
    Do
        tmpGroup = Everything that is NOT an Opening HTML Delimeter
        If the Text following tmpGroup Does NOT Equal "</" & Group(1) Then
            tmpGroup += "<"
            retGroup += tmpGroup
        Else
            Exit Do
        End If
    Loop
    Return retGroup
End Function

Nochmals für die HTML-Papageien: Parsing HTML

Dies zeigt einen einfacheren regulären Ausdruck, der Schleifen und Bedingungen (Algorithmen?) Durchführt. Das einzige, was fehlt, ist die tatsächliche mathematische Berechnung. Dies ist ein detaillierterer regulärer Ausdruck, der nur eine TD Zelle effizienter als die typische "(. *?)" - Methode abruft.

Aber selbst als Regex-Enthusiast und selbsternannter Meister würde ich niemandem sagen, dass Regex eine Programmiersprache ist. Mein eigenes Argument gegen mich selbst ist, dass es nicht alleine stehen kann, sondern über eine eigene Engine ausgeführt werden muss, während es von einer anderen Programmiersprachen-Engine unterstützt wird.

0
Suamere