Quali modelli di calcolo possono essere espressi attraverso le grammatiche?


18

Questa è una riformulazione dei programmi grammaticali Are? precedentemente chiesto da Vag e con molti suggerimenti dai commentatori.

In che modo una grammatica può essere vista come specifica di un modello di calcolo? Se, ad esempio, prendiamo una semplice grammatica senza contesto come

G ::= '1' -> '0' '+' '1'
      '1' -> '1' '+' '0'
      '2' -> '2' '+' '0'
      '2' -> '1' '+' '1'
      '2' -> '0' '+' '2'
      '3' -> '3' '+' '0'
      '3' -> '2' '+' '1'
      '3' -> '1' '+' '2'
      '3' -> '1' '+' '2'

Supponendo che il parser non distingua tra simboli terminali e non terminali, come ho dimostrato qui, è possibile eseguire una semplice aritmetica per numeri fino a 3.

Ad esempio, prendi la stringa

"2 + 0 + 1"

L'esecuzione di un parser LR (1) su questa stringa dovrebbe fornire il seguente albero di sintassi concreto in cui il risultato del calcolo è memorizzato nella radice dell'albero:

           '3'
         /  |  \
        /   |   \
      '2'  '+'  '1'
     / | \
    /  |  \
  '2' '+' '0' 

Quindi, se prendiamo una grammatica per essere un programma e un generatore di parser per essere un compilatore , potremmo vedere il linguaggio di specifica grammaticale come un linguaggio di programmazione ?

Inoltre, potremmo costruire programmi completi di Turing specificando grammatiche simili a come potresti costruire programmi completi di Turing con automi celullari o il calcolo lambda ?

In altre parole, è noto che, nel senso del riconoscimento di una lingua, le lingue regolari corrispondono agli automi a stati finiti , le lingue senza contesto corrispondono agli automi push down e le lingue sensibili al contesto corrispondono agli automi limitati lineari . Tuttavia, se consideriamo le grammatiche come dispositivi computazionali (cioè programmi nel senso dell'esempio sopra), allora come classifichiamo la forza computazionale di ogni classe di grammatiche nella gerarchia di Chomsky?

Inoltre, che ne dici delle sottoclassi meno conosciute di grammatiche come

EDIT: A proposito, questo è un pignolo sulla mia stessa domanda, ma non ho detto che non ho dato alcun simbolo di partenza per la grammatica di esempio e ho agitato a mano la necessità di distinguere tra terminali e non terminali. Tecnicamente o tradizionalmente penso che la grammatica dovrebbe probabilmente essere scritta in una forma più complicata come questa (dove S è il simbolo iniziale e $ rappresenta il terminale di fine flusso):

G ::= S -> R0 '$'
      S -> R1 '$'
      S -> R2 '$'
      R0 -> '0'
      R0 -> R0 '+' '0'
      R1 -> '1'
      R1 -> R0 '+' '1'
      R1 -> '1' '+' R0
      R1 -> R0 '+' '1' '+' R0
      R2 -> '2'
      R2 -> R0 '+' '2'
      R2 -> '2' '+' R0
      R2 -> R0 '+' '2' '+' R0
      R2 -> R1 '+' '1'
      R2 -> R1 '+' '1' '+' R0

... non che cambi davvero nulla, ma pensavo di doverlo menzionare.

EDIT: Qualcos'altro che mi è venuto in mente quando ho letto la risposta di Gasche è che ogni ramo dell'albero nel mio esempio rappresenta un sottocalcolo. Se guardi ogni regola di produzione come una funzione in cui LHS rappresenta il risultato e RHS rappresenta i suoi argomenti, la struttura della grammatica determina come sono composte le funzioni.

In altre parole, il contesto del parser insieme al suo meccanismo lookahead aiuta a determinare non solo quali funzioni applicare ("kinda" come il polimorfismo parametrico) ma come dovrebbero essere composte insieme per formare nuove funzioni.

Almeno, immagino che potresti guardarlo in questo modo per CFG inequivocabili, per altre grammatiche la ginnastica mentale è un po 'troppo per me in questo momento.


3
Hai dimenticato di menzionare Visible Pushdown Automaton (Nested Words), un apparecchio così bello e promettente! È importante perché sembra essere un miglioramento minimo rispetto ai regexps per poter analizzare i programmi scritti nei linguaggi di programmazione più diffusi. ( cis.upenn.edu/~alur/nw.html )
Vag

1
Grazie, è molto interessante, non l'ho cercato! Ci sono un paio di altri che ho anche saltato come deterministici senza contesto, adiacenti agli alberi, indicizzati e così via, ho solo pensato che potrebbe essere un po 'troppo per una domanda ... Ma forse li aggiungerò
Rehno Lindeque

1
@imz Intendo grammatiche come sono formalmente definite nella gerarchia chomsky (vale a dire come serie di produzioni). Dato che sto affermando esattamente quello che stai dicendo: che le grammatiche sono programmi, significa solo la classe di programmi rappresentabile dalle grammatiche (che è la domanda).
Rehno Lindeque

1
@imz Ad essere sincero, non ho familiarità con le grammatiche indicizzate, le ho solo aggiunte come ripensamento.
Rehno Lindeque,

1
Sto iniziando a pensare che sarebbe stata una buona idea pubblicare questa domanda nel forum LtU invece di guardare le discussioni interessanti: P. A proposito @imz, forse sarebbe meglio leggere la domanda come "quali classi di grammatiche corrispondono a quali classi di programmi in senso" funzionale "descritte da Jukka nella risposta di Marc Hamman". Forse dovrei renderlo più chiaro però ...
Rehno Lindeque,

Risposte:


10

Esiste una corrispondenza uno a uno tra grammatiche di tipo 0 di Chomsky e macchine di Turing.

Questo è sfruttato nel linguaggio di programmazione Thue che consente di scrivere programmi completi di Turing specificati da una stringa iniziale e da una serie di regole di riscrittura delle stringhe (una grammatica semi-Thue , che equivale a una grammatica di tipo 0).

AGGIORNARE:

Oltre ai linguaggi esoterici di "Turing tar-pit" come Thue, è possibile utilizzare vari linguaggi di uso generale che consentono al programmatore di estendere la propria sintassi per eseguire il calcolo completo di Turing durante la fase di analisi-compilazione.

Le lingue della famiglia Lisp , in particolare Common Lisp , sono probabilmente gli esempi più ovvi, ma anche, a grandi linee, le lingue con controllo statico del tipo che non è sempre necessario arrestare, come C ++ con template , Scala e Qi .


Ma la domanda riguarda le cose che funzionano al contrario: si dovrebbe arrivare al risultato non riscrivendo una sequenza iniziale di simboli secondo le regole, ma il "risultato" del calcolo definito da una grammatica in questa domanda è un iniziale simbolo che può produrre la sequenza "input" secondo le regole della grammatica.
imz - Ivan Zakharyaschev

2
concat(quote(in),out)TM(in)=out

Concordo sul fatto che la corrispondenza tra grammatiche di tipo 0 e TM è una risposta valida alla domanda (specialmente se limitata al calcolo delle funzioni sì / no). L'ulteriore suggerimento di modellare una TM arbitraria con una grammatica introducendo una convenzione su come rappresentare le coppie input-output mi sembra non corrispondere all'interesse previsto della domanda originale: (continua)
imz - Ivan Zakharyaschev

Lo capisco come una domanda per sfruttare esattamente i quadri grammaticali esistenti e i corrispondenti parser per eseguire calcoli, cioè la forma consentita della traduzione tra una funzione fe una grammatica può essere solo: un input che sono stato analizzato come S significa f ( I) = S.
imz - Ivan Zakharyaschev

1
A livello superficiale, il linguaggio di programmazione Thue non sembra rientrare in questo tipo di utilizzo di un framework grammaticale: sebbene abbia regole di riscrittura come una grammatica, il calcolo di un risultato da un input va nella direzione delle regole, non al contrario direzione, come vuole Rehno. (Ma forse è solo una questione di cambiare la direzione delle frecce nelle produzioni: tradurre in grammatica una "computazione come un parser" nel senso di questa Q potrebbe essere solo cambiare le direzioni delle regole, quindi il programma Thue arriveranno ai simboli di partenza come i risultati, no? ..)
imz - Ivan Zakharyaschev

6

La mia risposta non intende essere formale, precisa e assolutamente in argomento. Penso che la risposta di Marc Hamman sia solida, ma la tua domanda mi ha fatto pensare a un argomento correlato.

Le grammatiche possono essere considerate casi speciali di sistemi deduttivi: l'input è un giudizio e l'albero di analisi è una derivazione del giudizio, o prova che il giudizio è valido secondo le regole (grammaticali).

In tal senso, la tua domanda potrebbe essere correlata all'approccio di alcune parti della comunità di programmazione logica / ricerca di prove (sto pensando ad esempio a Dale Miller ), ovvero che la ricerca di prove ha contenuti computazionali, al contrario del più classico teoria del tipo / teoria delle prove in cui il calcolo è la normalizzazione delle prove .

Nota: rileggendo la mia risposta, penso che l'idea che "la costruzione di alberi di analisi sia una ricerca di prove" sia un po 'inverosimile qui. La ricerca della prova scorre piuttosto nella direzione opposta: una parte da un giudizio dato piuttosto complesso e, con l'uso ripetuto delle regole di inferenza che lavorano sulla struttura della prova, si spera di ottenere assiomi più semplici che non devono essere ulteriormente dimostrati. Quindi sarebbe più naturale vedere, in termini grammaticali, giudizi complessi come non terminali, atomi come terminali e la ricerca di prove come un problema di generazione di parole o test di non vuoto.


Osservazioni molto interessanti però. Il mio cervello è un po 'troppo stanco per dare una buona risposta in questo momento, tuttavia nel mio esempio i rami dell'albero rappresentano essenzialmente sub-calcoli che sono composti insieme secondo le regole di analisi ...
Rehno Lindeque,

6

Inoltre, potremmo costruire programmi completi di Turing specificando le grammatiche ...?

Non sono sicuro di aver compreso correttamente la tua domanda, ma se stai cercando un linguaggio di programmazione basato su una sorta di sistema di riscrittura delle stringhe, probabilmente ti interesserebbe Refal , che si basa sul formalismo dell'algoritmo Markov (un Turing- formalismo completo che è anche un sistema di riscrittura delle stringhe simile alla grammatica).


1
Ho capito la domanda nel modo seguente: Rehno è interessato al processo di analisi del bootom-up (definito da una grammatica) per essere visto come un calcolo del risultato. Il calcolo dovrebbe costruire il risultato dalle parti andando nella direzione opposta alle regole di produzione della grammatica. Le regole di riscrittura di Refal (IIUC, analogamente al linguaggio di programmazione Thue sopra menzionato) andrebbero nella direzione opposta (dall'input al risultato).
imz - Ivan Zakharyaschev

Ora che ci penso, le grammatiche sensibili al contesto hanno più di un simbolo sull'LHS delle regole di produzione. Quindi penso che non ci sia vera differenza pratica. Un parser per un linguaggio sensibile al contesto sarebbe un sistema di riscrittura delle stringhe, non importa come lo guardi nel modo giusto?
Rehno Lindeque,

@imz grazie per il chiarimento sulla domanda di Rehno. @Rehno "Un parser per un linguaggio sensibile al contesto sarebbe un sistema di riscrittura delle stringhe, non importa come lo guardi nel modo giusto?" - probabilmente ha senso, sì.
Artem Pelenitsyn,

Ma le regole di riscrittura di Refal sono trattate in modo non deterministico? (O in altre parole: Refal farà backtracking nella ricerca di un percorso di riscrittura funzionante?) Se vogliamo modellare questo approccio di "analisi come calcolo" con regole di riscrittura in senso inverso, abbiamo bisogno di regole non deterministiche; considera una grammatica come S -> A a; S -> B b; A -> 0; B -> 0. Se programmiamo questo invertendo le regole, dovremo scegliere regole diverse per l'elaborazione 0in fase di esecuzione al fine di valutare "0a" o "0b" su S.
imz - Ivan Zakharyaschev,

6

(Solo alcune considerazioni banali. Potrebbe essere un commento, ma troppo lungo.)

In effetti, ciò che descrivi sembra in effetti la visione molto naturale di cosa sia una lingua (nella comprensione umana del "linguaggio", del suo scopo) e di come una grammatica definisce una lingua.

Un linguaggio comprende (infinitamente molte) forme sintattiche corrette che vengono interpretate per dare i valori semantici .

Se l'interpretazione è calcolabile, le forme sintattiche di una lingua possono essere viste come programmi che calcolano i valori semantici.

Se assumiamo che una lingua sia implementata come un dispositivo finito, possiamo chiamare questa rappresentazione finita di una lingua una "grammatica". Secondo questa comprensione, una grammatica si preoccupa della sintassi, ma anche della semantica, cioè come calcolare il valore semantico di un'intera espressione dai valori delle sue parti (le parti atomiche e i loro valori sono memorizzati in un "lessico") .

Alcune teorie del linguaggio naturale hanno una tale forma (la forma che è coerente con le considerazioni di cui sopra; è già stata menzionata nella risposta di @ gasche qui): un sistema deduttivo che cerca una derivazione dell'input (accoppiato con il calcolo del semantico valore o la costruzione del termine di prova; cfr. corrispondenza Curry-Horward). Quindi, se guardiamo a sistemi del genere e li consideriamo grammatiche, allora la tua domanda è banale : questi sistemi sono concepiti esattamente per eseguire calcoli nel modo che descrivi.

fGI f(I)=SISG

(In effetti, i veri compilatori per i linguaggi di programmazione sembrano più un sistema con sintassi e semantica: trasformano la forma sintattica di un programma in un eseguibile, che è il significato semantico del programma, e piuttosto non semplicemente un simbolo iniziale della grammatica.)


4

Solo per aggiungere:

Un programma di pura logica ha una lettura dichiarativa e una lettura procedurale. Questo rapporto discute l'idea che questi possono essere integrati da una lettura grammaticale, in cui le clausole sono considerate come regole di riscrittura di una grammatica. L'obiettivo è dimostrare che questo punto di vista facilita il trasferimento di competenze dalla programmazione logica ad altre ricerche sui linguaggi di programmazione e viceversa. Alcuni esempi di tale trasferimento sono discussi. D'altro canto, la visione grammaticale presentata giustifica alcune estensioni ad hoc alla pura programmazione logica e facilita lo sviluppo di basi teoriche per tali estensioni.

Una visione grammaticale della programmazione logica di Pierre Deransart e Jan Maluszynski.


Apparentemente, Prolog è nato dalle grammatiche degli attributi, quindi questa visione è ciò che ha iniziato la programmazione logica.
reinierpost,

1

Che dire di qualcosa come i numeri di Peano:

S    -> int
int  -> zero
int  -> succ
zero -> "0"
succ -> "#" int

riconoscerà qualsiasi stringa (numero) di questo modulo:

0   // zero
#0  // one
##0 // two

e dovrebbe restituire una struttura nidificata, con la profondità essere il numero.

Ma inizia a complicarsi quando si vuole implementare basta dire addizione:

S    -> int
int  -> sum
int  -> zero
int  -> succ
zero -> "0"
succ -> "#" int
sum  -> int "+" int

Ha perfettamente senso in quanto riconoscerà solo ints ben formati come questo:

#####0 + ####0

Ma questa grammatica introduce una divisione nell'albero di analisi ogni volta che c'è una somma, quindi invece di avere un bel albero a un ramo, che si associa direttamente a un numero, abbiamo la struttura dell'espressione, ancora a pochi calcoli dall'efficace valore. Quindi non viene eseguito alcun calcolo, solo il riconoscimento. Il problema potrebbe non essere la grammatica ma il parser. Si può invece usare qualcos'altro, idk ... Un altro punto che viene in mente è l'adeguatezza del formalismo grammaticale per esprimere il calcolo. Quando sembri un assioma di Peano (in notazione simile a Haskell):

1) Nat = Zero
2) Nat = Succ Nat
3) Sum ( Succ X ) ( Y ) = Succ ( X + Y )
4) Sum Zero X = X

La terza regola afferma esplicitamente una trasformazione. Qualcuno potrebbe immaginare di avere lo stesso significato in una regola grammaticale senza contesto. E se sì, come!?

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.