Le espressioni regolari sono un linguaggio di programmazione?


27

In senso accademico, le espressioni regolari si qualificano come linguaggio di programmazione?

La motivazione per la mia curiosità è una domanda SO che ho appena esaminato e alla quale è stato chiesto "Can regex do X?" e mi ha fatto chiedermi cosa si possa dire in senso generico delle possibili soluzioni che le utilizzano.

Praticamente chiedo: "le espressioni regolari di Turing sono complete"?


9
Quindi, in sostanza, stai chiedendo "le espressioni regolari di Turing sono complete"?
FrustratedWithFormsDesigner,

Sarebbe bello se qualcuno elaborasse in aggiunta, ma sì
Aaron Anodide il

4
Il "turing delle espressioni regolari complete" richiede una comprensione dei tipi di lingua e dell'herchary chomsky

5
(1 minuto più tardi di una modifica) e se vuoi seguire quel percorso di domande e spiegazioni, potresti voler dare un'occhiata allo scambio di teoria cs . Il lemma del pompaggio è la più semplice disproof per "può una lingua normale corrispondere a ^ nb ^ n" (che è abbinabile a una macchina di Turing).

1
Penso che stia chiedendo se può metterlo sul suo curriculum nella sezione "Linguaggi di programmazione". La risposta in quel caso è no. Questo va nella sezione "Tecnologie".
Neil,

Risposte:


46

Le espressioni regolari sono un particolare tipo di grammatica formale utilizzata per analizzare stringhe e altre informazioni testuali che sono note come "Lingue regolari" nella teoria del linguaggio formale. Non sono un linguaggio di programmazione in quanto tale. Sono più una scorciatoia per la codifica che altrimenti sarebbe estremamente noiosa da implementare e persino più confusa rispetto al Regex dall'aspetto talvolta arcano.

I linguaggi di programmazione sono in genere definiti come linguaggi completi di Turing . Tali lingue devono essere in grado di elaborare qualsiasi funzione calcolabile . Regex non rientra in questa categoria.

Se vuoi una lingua che assomigli a Regex, prova J.


1
+1, ho guardato ma non sono riuscito a trovare una buona discussione / confutazione della completezza di Turing delle espressioni regolari.
FrustratedWithFormsDesigner,

1
@ davidk01 - Gli automi cellulari possono essere completi (anche se i compilatori buoni sono difficili da trovare), le espressioni regolari no. Puoi fare calcoli non banali, sì, ma ci sono cose abbastanza banali che non puoi fare altrettanto. Turing di automi cellulari completi potrebbe essere considerato come un linguaggio di programmazione, poiché in linea di principio è possibile scrivere qualsiasi programma con essi come si potrebbe con qualsiasi altro linguaggio.
psr

1
È anche importante notare che la regex che esegue i test di primalità ( montreal.pm.org/tech/neil_kandalgaonkar.shtml#primality_regex ) utilizza funzionalità di perl regex che sono più potenti di "Espressioni regolari" in senso accademico - vale a dire, gruppi memorizzati . Le lingue normali non possono richiedere memoria arbitraria.
Eric W.,

5
@WorldEngineer: esistono linguaggi di programmazione interessanti e utili che non sono completi di Turing. Datalog, SQL e ACL2 sono alcuni esempi che vengono in mente, così come un numero qualsiasi di calcoli lambda fortemente normalizzanti utilizzati in cose come i dimostratori di teoremi basati sulla teoria dei tipi.
Ryan Culpepper,

1
Non tutti i linguaggi di programmazione sono completi. Ad esempio, linguaggi dichiarativi privi di contesto come XML che non sono completi senza essere associati a un interprete potrebbero essere considerati linguaggi di programmazione. Tutto dipende dalla tua definizione di "linguaggio di programmazione". Tutto ciò di cui hai bisogno per trasformare una lingua "normale" in una lingua "senza contesto" è uno stack push-down. Poi sono le tartarughe fino in fondo.
Evan Plaice,

14

E 'difficile rispondere a domande di tipo "è X un Y ", se i partecipanti dell'uso dibattito diverse definizioni di X e Y . Potrebbe essere che per alcune definizioni, la risposta sia "sì" e per alcune definizioni la risposta sia "no". Soprattutto se la risposta dipende da dettagli tecnici in cui le diverse definizioni differiscono. Anche questa discussione contiene alcune informazioni sbagliate, quindi per favore abbi pazienza con una risposta più lunga.

Cosa intendiamo per " linguaggio di programmazione "?

Una semplice risposta potrebbe essere "una lingua utilizzata per creare programmi". Certo, ma: che tipo di programmi? Che dire di una lingua che potrebbe essere utilizzata per creare alcuni tipi di programmi, ma non altri tipi di programmi? Ecco due esempi specifici per illustrare i casi estremi:

1) Un linguaggio immaginario chiamato M funziona così: Se il programma contiene la singola lettera "m", crea un gioco di Minesweeper. Tutto il resto è un errore di sintassi.

Intuitivamente, questo non è ciò che intendiamo dicendo "un linguaggio di programmazione". Ma il dipartimento marketing di M potrebbe sostenere che soddisfa tecnicamente la definizione, perché può essere utilizzato per creare un programma. Certo, il compilatore fa alcune parti critiche per te, ma è quello che fanno i compilatori, vero? Un compilatore del linguaggio C traduce anche alcune semplici parole in dozzine di istruzioni del processore. Il compilatore M va oltre e rende il tuo lavoro ancora più semplice.

2) Se installi la versione originale del famoso Turbo Pascal, puoi scrivere molti tipi di programmi. Ma non puoi scrivere un gioco che gira nel browser web, perché l'API necessaria semplicemente non c'è.

Quindi qual è esattamente la cosa che rende Turbo Pascal un linguaggio di programmazione, ma M non ce l'ha? In parole semplici, puoi fare di più in Pascal che in M. Ma immagina di avere un M.NET, che crea un gioco Minesweeper in esecuzione in un browser web. Quindi ora abbiamo qualcosa che Pascal può fare e M.NET non può, ma abbiamo anche qualcosa che M.NET può fare e Pascal non può. Perché dovremmo considerare importanti i vantaggi di Pascal e irrilevanti i vantaggi di M.NET?

La risposta è che puoi scrivere tutti i tipi di algoritmi in Pascal, ma non puoi scrivere algoritmi in M o M.NET. Certo, M compila il tuo comando "m" e C compila il tuo comando "strcmp". Ma puoi inserire "strcmp" in un contesto più ampio, ad esempio confrontare due file riga per riga oppure leggere migliaia di stringhe e ordinarle in ordine alfabetico o ... beh, milioni di altre cose. Ed è proprio questa capacità di utilizzare determinati comandi in qualsiasi algoritmo che costituisce l'essenza di un linguaggio di programmazione.

Che cos'è esattamente un algoritmo e, cosa più importante, che cos'è "qualsiasi algoritmo"? Nell'informatica usiamo le parole Turing-complete . L'idea è che esiste un insieme di linguaggi per computer, in cui ciascuno di essi è in grado di simularli tutti. Una di quelle lingue è la macchina di Turing, motivo per cui vengono chiamate così. Pascal è lì, C c'è, Java c'è, Python c'è, Lisp è presente, Smalltalk è presente, anche XSLT è presente. Le nostre ipotetiche M e M.NET non ci sono. Puoi saperne di più in qualsiasi università fornendo un decente corso di informatica, ma l'idea è che un linguaggio completo di Turing può fare qualsiasi cosache può fare un'altra lingua completa di Turing, se si fornisce loro l'API minima necessaria. (Se dai qualche API del browser Web a Pascal, puoi creare tutti i tipi di giochi nel browser Web. Se dai l'API del browser Web a M, sei ancora in grado di creare Minesweeper.) Potremmo dire metaforicamente che se rimuovi tutte le API da un linguaggio di programmazione, l'importante è ciò che rimane.

Cosa intendiamo con " espressioni regolari "?

Diversi linguaggi di programmazione li implementano in modo leggermente diverso. Ma l'idea originale era che le espressioni regolari esprimessero i cosiddetti linguaggi regolari . Nota che qui non parliamo di linguaggi di programmazione, ma di linguaggi (pseudo-) umani. Immagina di trovare una tribù esotica che parla una lingua composta solo da parole "ba", "baba", "bababa" e così via. Potresti descrivere verbalmente questo linguaggio come "una sillaba 'ba' ripetuta una o più volte" o usando un'espressione regolare come "(ba) +".

Le espressioni regolari dovrebbero esprimere: "niente", "questa lettera", "questo, seguito da quel", "questo o quello", "questo, ripetuto una o più volte", e "non questo". - Questa è la definizione matematica . Nient'altro è solo una comoda scorciatoia costruita dai componenti precedenti. Ad esempio "questo, ripetuto due o tre volte" può essere tradotto come "questo, seguito da questo, seguito da (questo o niente)", ma potrebbe essere più conveniente scrivere "ba {2,3}" rispetto a "baba (ba)?".

Nella vita reale, un'implementazione tipica di "espressioni regolari" implementa più di questo. Ad esempio, usando la definizione matematica, un linguaggio di "aba", "aabaa", "aaabaaa" e così via - qualsiasi numero di "a" s, seguito da una "b", seguito dallo stesso numero di "a "s - non è un linguaggio normale. Tuttavia, molte "espressioni regolari" utilizzate oggi potrebbero rilevarlo, usando il concetto aggiuntivo di "la stessa cosa che abbiamo trovato prima", scritto come "(a +) b \ 1". Usando questo concetto aggiuntivo, possiamo fare alcune cose interessanti, ad esempio rilevare parole costituite da un numero primo di lettere. Tuttavia, non possiamo fare alcun algoritmo ... per una spiegazione del perché,

Quindi, tornando all'argomento originale: le espressioni regolari (definite come: espressioni che descrivono i linguaggi regolari nella gerarchia di Chomsky; o come: la prima, più l'operazione \ 1) sono un linguaggio di programmazione (definito come: Turing-complete)? La risposta è no . No, non è possibile implementare alcun algoritmo utilizzando espressioni regolari e la capacità di implementare qualsiasi algoritmo è ciò che le persone che studiano informatica in genere comprendono come l'essenza del linguaggio di programmazione.

Naturalmente, chiunque può cambiare la risposta insistendo su una definizione diversa . Come ho scritto all'inizio, i dettagli tecnici sono importanti qui. Se sbagli, ottieni una risposta sbagliata.

E se siete non è interessato ai dettagli tecnici, la risposta potrebbe essere: È possibile utilizzare le espressioni regolari (e nient'altro) per fare un programma? No. Quindi perché chiamarlo un linguaggio di programmazione? (Tuttavia, una risposta come questa è stata scaricata ed eliminata qui, motivo per cui ho scritto questa versione più lunga.)

EDIT: Inoltre, chiunque può creare una libreria implementando la propria nuova variante di "espressioni regolari" con alcune nuove funzionalità aggiunte. Ad un certo momento, le nuove funzionalità potrebbero essere sufficienti affinché l'intero sistema diventi Turing completo. Un esempio banale sarebbe incorporare un linguaggio completo di Turing usando una nuova sintassi; ma può anche succedere meno ovviamente. Forse è già successo.


0

In .Net, Regex non solo può gestire più forme di condizionali, usando diverse combinazioni di alternanza e lookaround, ma può anche manipolare il proprio stack.

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

Questo, ad esempio, è un piccolo frammento che ho scritto per recuperare una tabella HTML. A differenza di altri motori regex, controlla la pila di raccolte di acquisizione (push, peek e pop) e può gestire oggetti nidificati. Ne ho uno più complesso, ma è un po 'proprietario.

Penso che in questo esempio, Regex possa essere considerato avere tutti i requisiti di base di un linguaggio di programmazione. Ha variabili, memoria incorporata, condizionali, input e output, si compila utilizzando uno dei motori di compilazione regex multipli (.Net in questo caso).

In risposta al sovrautilizzato squawking di (MAI) analisi HTML con Regex, sono andato avanti e ho pubblicato una risposta pre-digitata che posso pubblicare: Analisi HTML

Un esempio di Anoter (solo una dimostrazione) è il seguente:

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

Ancora una volta, per i pappagalli HTML: analisi HTML

Questo mostra un regex più semplice che esegue loop e condizionali (algoritmi?). L'unica cosa che manca è il calcolo matematico reale. Questa è un'espressione regolare più dettagliata che estrae una cella TD in modo più efficiente rispetto al tipico metodo "(. *?)".

Ma anche come appassionato di Regex e autoproclamato maestro, non vorrei andare in giro dicendo a nessuno che Regex è un linguaggio di programmazione. Il mio argomento contro me stesso è che non può stare da solo, deve essere gestito attraverso il proprio motore pur essendo supportato da un altro motore di linguaggio di programmazione.


Se "collaudi" questo e non funziona, devi capire che la maggior parte dei "tester" di regex engine non gestiscono .Net Regex (Balancing Groups). Dovresti effettivamente usarlo in un programma .Net.
Suamere,

3
Oh Dio, questa è una prova prima facia del perché non dovresti mai usare regex per analizzare l'html . Mai.
Tacroy,

@Tacroy È bello vedere qualcuno interpellato per consigli sui pappagalli sull'analisi dell'HTML con regex. Sebbene non sia per i deboli di cuore, combinare regex come quella sopra con uno stack è una ricetta di base (ed efficiente) per costruire un parser privo di contesto.
Evan Plaice,

1
In risposta al Parrot Squawking. Ho creato questo: Parsing HTML
Suamere

Non è un'espressione regolare se accetta linguaggi sensibili al contesto. È un altro DSL che è un superset di Regex. Il nome del fornitore non cambia questo
Caleth

0

Sebbene trovare / sostituire in espressioni regolari non sia un linguaggio di programmazione completo di Turing, come spiegato dalle risposte precedenti, se si consente di utilizzare azioni ripetute di sostituzione con espressioni regolari, quindi sì, è possibile codificare qualsiasi macchina di Turing utilizzando l'espressione regolare:

La ripetuta ricerca / sostituzione con espressioni regolari è un linguaggio di programmazione completo di Turing

Di conseguenza, è possibile calcolare qualsiasi funzione calcolabile utilizzando la stessa ricerca e sostituire ripetutamente l'espressione regolare javascript.

Per dimostrare la completezza di turing, è sufficiente codificare una macchina di Turing nella ricerca / sostituzione di espressioni regolari. Supponiamo che lo stato dell'editor sia:

0000#12345:01-5:0#0000000

che può essere letto come un nastro di simboli con sopra un lettore:

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

Per la regola che legge 0 nello stato 5, scrive 1 e cambia il suo stato in 3 e si sposta a sinistra, lo astraggiamo usando la seguente notazione:

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

Codifichiamo la notazione precedente in un'espressione regolare di ricerca:

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

e la sua espressione sostitutiva (simile a JavaScript)

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

Ok, ora come codificare molte regole? Usiamo la concatenazione con l' oroperatore |per la ricerca di espressioni regolari e combiniamo i risultati in sostituzione, numerando i numeri di gruppo con gli offset. Ad esempio, consideriamo l'insieme di quattro regole.

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

Li codifichiamo in una ricerca e sostituiamo l'espressione:

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

Provalo nel tuo motore javascript preferito:

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)
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.