Linguaggi, stringhe e alfabeti
Linguaggi e grammatiche Un linguaggio è un sistema di comunicazione tra persone che permette di trasmettere informazioni attraverso un sistema di simboli (detto alfabeto) combinati in accordo alle regole di una grammatica. Descrivere la sintassi di un linguaggio significa stabilire un criterio che consenta di decidere se una frase appartiene o meno al linguaggio. I modi possibili sono: descrivere le proprietà che le parole del linguaggio devono soddisfare (approccio algebrico); descrivere un algoritmo capace di riconoscere le parole di un linguaggio (approccio riconoscitivo); descrivere un dispositivo, le grammatiche, in grado di generare tutte e sole le possibili frasi del linguaggio (approccio generativo).
Alfabeto (1) Un alfabeto è un insieme non vuoto e finito di simboli. Vediamo alcuni esempi: Alfa1={ 0,1,2,3,4,5,6,7,8,9} alfabeto utilizzato dal sistema decimale per la rappresentazione dei numeri naturali. Alfa2 = { A,B, C, D, ….} + { a,b,c,d,e,f} alfabeto utilizzato per rappresentare frasi in italiano. Alfa3 = {0, 1} alfabeto usato per rappresentare numeri in binario. Alfa4 = {FOR, IF, (, ), ELSE, WHILE, ….} alfabeto del linguaggio Java.
Alfabeto (2) Una stringa o parola di un alfabeto A è la concatenazione di un numero finito di simboli dell’alfabeto A. Consideriamo gli alfabeti: Alfa1, Alfa2, Alfa3, Alfa4. 4567è un esempio di stringa di Alfa1 cioè una stringa dell’alfabeto decimale Montagnaè un esempio di stringa di Alfa2 cioè una stringa dell’alfabeto italiano è un esempio di stringa di Alfa3 cioè una stringa dell’alfabeto binario if(è un esempio di stringa di Alfa4 cioè una stringa del linguaggio Java La lunghezza di una stringa s è il numero di caratteri che la costituiscono. La stringa vuota Ɛ (epsilon) è quella di lunghezza zero (cioè non costituita da alcun simbolo). Consideriamo i precedenti esempi di stringhe. 4567 (stringa di Alfa1) ha lunghezza 4 Montagna (stringa di Alfa2) ha lunghezza 8 (stringa di Alfa3) ha lunghezza 6 if( (stringa di Alfa4) ha lunghezza 3
Concatenazione e chiusura Ricordiamo che per concatenazione (indicata con l’operatore °) di due qualsiasi stringhe a e b intendiamo la giustapposizione delle due stringhe, cioè lo scriverle l’una di seguito all’altra. Per esempio, se a = “ciao” e b = “ mondo”, la concatenazione a°b = “ciao mondo”. L’operazione di concatenazione è associativa ma non commutativa. Associativa : ((abb ° b) ° ba) = (abb ° (b ° ba)) = abbbba Non commutativa : abb ° bba ≠ bba ° abb abbbba bbaabb salva ° gente ≠ gente ° salva salvagente gentesalva L’insieme di tutte le stringhe definite sull’alfabeto A (inclusa la stringa vuota) è denotato con A* ed è detta chiusura (o chiusura transitiva) di A. La chiusura senza stringa vuota è detta chiusura positiva e si denota con A +. Quindi: A* = A + U { Ɛ }.
Operazioni sugli alfabeti: prodotto Dati X e Y due insiemi di stringhe su un alfabeto A si definisce prodotto tra questi due insiemi (e si indica sempre con l’operatore *) l’insieme Z così definito: Z = X*Y = { x°y / x appartiene X e y appartiene Y}. Cioè l’insieme formato da tutte le combinazioni della concatenazione delle stringhe di X alle stringhe di Y. Consideriamo per esempio, i seguenti insiemi: X = { 00, 01, 11} Y = {0, 01} Il prodotto Z = X *Y sarà: Z = { 000, 010, 110, 0001, 0101, 1101} cioè tutte le stringhe risultato delle possibili combinazioni aventi come prima parte le stringhe di X e come seconda parte le stringhe di Y. Possiamo legare il prodotto di due insiemi (e quindi l’operazione di concatenazione) alla chiusura di lunghezza n. Osserviamo le seguenti uguaglianze. A 0 = { Ɛ } A 1 = A ° A 0 = A 0 ° { Ɛ } = A (cioè per ogni stringa x, x ° ε = ε ° x = x) A 2 = A ° A A 3 = A ° A ° A
Una prima grammatica Uno dei modi per descrivere un linguaggio è quello di costruire un dispositivo in grado di generare solo le frasi valide del linguaggio. Per frase intendiamo un insieme di parole (o stringhe) del linguaggio che hanno un significato ammissibile per quel determinato linguaggio (corretta sintatticamente ma non semanticamente). Per questo approccio generativo utilizziamo le grammatiche. Una grammatica è un insieme di regole che le frasi del linguaggio devono soddisfare. Nella grammatica italiana una frase può assumere, per esempio, la forma: Soggetto — Predicato — Complemento Il soggetto a sua volta può assumere la forma: Articolo — Nome E così via. Articolo, Nome, Predicato, Complemento sono variabili del discorso.
Una definizione formale di grammatica (1) Una grammatica G è una quadrupla G = (AN, AT, S, P), dove: AN è l’insieme finito dei simboli non terminali; AT è l’insieme finito di simboli terminali detto alfabeto; S è il simbolo iniziale o distintivo o assioma, S appartiene a AN. P è un insieme di coppie ( α, β ) dette produzioni espresse nella forma α β (si legge da α si deriva β ) oppure nella forma alternativa α ::= β α è la parte sinistra della produzione, deve essere una stringa non vuota contenente almeno un simbolo non terminale; β è la parte destra, può contenere sia simboli terminali sia simboli non terminali. Se esistono più produzioni con la parte sinistra uguale α β, α γ, α δ allora si può scrivere: α β | γ | δ
Una definizione formale di grammatica (2) Facciamo un esempio. Considera la seguente grammatica descritta in BNF che genera, in formato binario, solo numeri pari. Cioè numeri che terminano con 0 (110, 11010, 11110, ecc.). ::= 1 0 ::= Ɛ | 0 | 1 La nostra grammatica G è così definita: G=( AN, AT, P, S) dove: AN = {, } AT { 0, 1, Ɛ } S = P = { (, 1 0), (, Ɛ ), (, 0 ), (, 1 ) } Da notare la struttura tipicamente ricorsiva di alcune produzioni. In queste produzioni, cioè, il simbolo non terminale della parte sinistra appare anche nella parte destra. Le produzioni 3 e 4 sono produzioni ricorsive destre. Una produzione è: ricorsiva destra se ha la forma: α Aα con A appartenente a AN U AT e A ≠ 0; ricorsiva sinistra se ha la forma: α αA con A appartenente a AN U AT e A ≠ 0. 43
Il processo di derivazione Data una grammatica G = (AN, AT, S, P) e la produzione α β, diremo che: τβδ deriva immediatamente da ταδ (o ταδ genera immediatamente τβδ ) e scriviamo: τβδ ταδ se una stringa ταδ contenuta in {AT U AN } può essere riscritta come τβδ. Data una grammatica G = (AN, AT, S, P), diremo che: la stringa δ deriva dalla stringa τ o δ genera τ se esiste una successione di k derivazioni immediate tali che: τ α 1 α 2 α 3 …. α k δ e scriviamo: τ δ Consideriamo per esempio, la grammatica appena vista che genera, in formato binario, solo numeri pari. E analizziamo il seguente processo di derivazione dal simbolo iniziale nel quale sono evidenziate ad ogni passaggio il numero della produzione che è stata applicata. 1 0 10 0 ° prod. 3° prod.4° prod.2° prod. k
Linguaggio generato da una grammatica Il linguaggio generato da una grammatica G = (AN, AT, S, P) che indicheremo da ora con L(G) è l’insieme costituito da tutte le frasi di simboli terminali che si possono generare partendo dal simbolo iniziale S e applicando le produzioni P di G. Possiamo scrivere: L(G) = { w / w appartiene AT* e S w } Consideriamo, ad esempio, la solita grammatica che genera numeri pari. L(G) = { 0, 10, 100, 110, 1000, 1010, 1100, 1110, ….. } k
Classificazione delle grammatiche (1) Le grammatiche generative possono essere descritte e confrontate fra loro con l'aiuto di una classificazione proposta da Noam Chomsky negli anni ‘50. Questa definisce una serie di tipi di grammatiche formali aventi potere espressivo crescente. La classificazione, che si basa sulla forma delle produzioni della grammatica, riguarda anche il relativo linguaggio che esse generano.
Classificazione delle grammatiche (2) Grammatiche e linguaggiProduzioni assumono la forma : di Tipo 0 o Generaliα β con α e β appartiene A T * U A N * cioè α e β sono stringhe di terminali e non terminali di Tipo 1 o Dipendenti dal contesto τAδ τβδ con τ e δ appartiene A T * U A N * A appartiene A N β appartiene A T + U A N + (β non deve essere uguale alla stringa vuota) cioè A è un non terminale e τ, β, δ sono stringhe qualsiasi di terminali e non terminali (ma β non deve essere vuota) di Tipo 2 o Libere dal contesto A α con A appartiene A N e α appartiene A T + U A N + cioè A deve essere un unico simbolo non terminale e α è una tsringa di terminali e non terminali (α non deve essere vuota) di Tipo 3 o Lineari o RegolariA α B oppure A α con A e B appartiene A N cioè A e B sono simboli non terminali, α è una stringa di terminali (α può anche essere vuota)
Esempi di grammatiche Esempio di grammatica regolare Il linguaggio L(G) = { anbm | n, m ≥ 0} cioè un numero qualsiasi di a seguito da un numero qualsiasi di b ma non necessariamente uguali (aabbb, abbb, aaaabb, aabb, ….); è generato dalla seguente grammatica. A aA | B B bB | Ɛ Esempio di grammatica libera dal contesto Il linguaggio: L(G) = { anbn | n ≥ 0} cioè stesso numero di a e b (ab, aabb, aaabbb...); è generato dalla seguente grammatica. S aSb | Ɛ Esempio di grammatica non libera Il seguente linguaggio: L(G) = { anbncn | n ≥ 0} stesso numero si a, b e c (abc, aabbcc, aaabbbccc, ….); non può essere generato da alcuna grammatica libera dal contesto.
Esempio di un linguaggio generato da grammatiche di tipo diverso L(G) = { a n oppure b n | n pari e n ≥ 4} cioè un numero pari di a o di b maggiore o uguale a 4 Grammatica di Tipo 0 S aaSaa | bbSbb aSa aaSaa | aa | Ɛ bSb bbSbb | bb produzioni di qualsiasi tipo Grammatica di Tipo 1 S aSa | bSb aSa aaSaa | aaaa bSb bbSbb | bbbb produzioni del tipo τAδ τβδ con A non terminale e τ, β, δ sono stringhe qualsiasi di terminali e non terminali (ma β non deve essere vuota) Grammatica di Tipo 2 S AA | BB A aaA| aa B bbB | bb A sinistra delle produzioni un solo simbolo non terminale. A destra qualsiasi simbolo (purchè non ci sia Ɛ ) La prima produzione, ad esempio, S AA (non può essere di Tipo 3) Grammatica di Tipo 3 S aaA | bbS A aaA | aa B bbB | bb A sinistra delle produzioni un solo simbolo non terminale. A destra produzioni del tipo A α B oppure A α con α stringa di terminali
Alberi sintattici e grammatiche ambigue Il processo di derivazione di una stringa a partire al simbolo iniziale può essere rappresentato graficamente attraverso gli alberi sintattici (parse tree) Ɛ Data una grammatica G il suo albero sintattico avrà le seguenti caratteristiche: alla radice dell’albero corrisponderà il simbolo iniziale della grammatica; a ogni nodo interno dell’albero corrisponderà un simbolo non terminale della grammatica; a ogni nodo foglia corrisponderà un simbolo terminale; a un nodo foglia può anche corrispondere il simbolo vuoto Ɛ. Consideriamo la solita grammatica vista precedentemente che genera numeri pari in formato binario. Le sue produzioni sono: ::= 1 0 ::= Ɛ | 0 | 1
Grammatiche ambigue Una stringa si dice ambigua se è generata da due distinti alberi sintattici. In tal caso anche la grammatica si dice ambigua. La grammatica G con le seguenti produzioni: X XbX | a Che genera il linguaggio L(G) costituito da stringhe che iniziano e finiscono per a e aventi a e b alternate: aba, ababa, abababa, ababababa. Ecc. Consideriamo adesso la stringa: ababa. Essa è ambigua poiché è possibile creare i due differenti alberi di derivazione mostrati di seguito. X XbX XbXa aa X XbX XbXa aa
Grammatiche ed automi Nella tabella che segue possiamo vedere l’abbinamento del tipo di automa riconoscitore ai vari tipi di grammatiche elencate nella classificazione di Chomsky. GrammaticheAutomi riconoscitori Tipo 0 (Generali)Macchina di Turing (sempre se L(G) è riconoscibile) Tipo 1 (Dipendenti dal contesto)Macchina di Turing (con nastro limitato) Tipo 2 (Libere dal contesto)Automa a pila Tipo 3 (Regolari)Automa a stati finiti (ASF)
Consideriamo l’automa riconoscitore M descritto dal diagramma degli stati mostrato a lato q1q1 q2q2 q3q3 1 1 A q0Aq0A Questo automa riconosce le sequenze di ingresso con un numero pari di 1 e di 0. Pertanto possiamo dire che il linguaggio riconosciuto da M è: L(M) = { numero pari di 0 e 1 } Lo stato q 0 è sia iniziale sia finale. La sequenza è riconosciuta solo se ci si trova in questo stato quando i caratteri in ingresso sono stati tutti letti.
Dall’automa alla grammatica regolare La grammatica G corrispondente si costruisce con il seguente algoritmo: l’insieme AT dei simboli terminali della grammatica coincide con l’insieme degli ingressi I dell’automa l’insieme dei simboli non terminali AN della grammatica coincide con quello Q degli stati dell’automa il simbolo iniziale S della grammatica è il simbolo corrispondente allo stato iniziale q0 dell’automa l’insieme delle produzioni si ottiene come segue: a ogni transizione dallo stato q i allo stato q j per effetto del carattere h, si crea una produzione Ni h Nj dove Ni e Nj sono simboli non terminali corrispondenti a q i e q j. Se q j Appartiene agli stati finali, si aggiunge la produzione Nj h Come possiamo notare il tipo di produzioni che si creano sono quelle che caratterizzano le grammatiche regolari (in particolare ricorsive destre). q0::= 1 q1 | 0 q3 | Ɛ q1 ::= 0 q2 | 1 q0 q2 ::= 0 q1 | 1 q3 q3 ::= 0 q0 | 1 q2 Poiché esiste la transizione: q 1 q 0 1 Poiché esiste la transizione: q 3 q 2 1