You are on page 1of 28

LINGUAGGI FORMALI E AUTOMI

Dispensa Universitaria
Quest'opera è stata rilasciata sotto la licenza Creative Commons Attribuzione - Non
Commerciale - Condividi allo stesso modo. Per leggere una copia della licenza visita il sito web
http://creativecommons.org/licenses/publicdomain/ o spedisci una lettera a Creative
Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
Linguaggio
Un linguaggio si basa sul concetto di alfabeto che è un insieme  composto da
simboli:
={a 1, a 2, a 3, a 4. ... a n }

La giustapposizione di questi simboli permette di generare gli elementi del


linguaggio, ossia le parole o stringhe.

La giustapposizione è un'operazione tale che avendo due simboli w e z allora


w∗z=wz . Di conseguenza l w∗z =l wl  z  . Il prodotto di giustapposizione gode
della proprietà associativa ma NON di quella commutativa.
● Associativa:  x∗y ∗z= x∗ y∗z 
● Commutativa: x∗ y≠ y∗x

Una parola o stringa è quindi una composizione di simboli dell'alfabeto e la sua


lunghezza l w è il numero di simboli che la compongono. Una parola con l w=0 è
detta parola vuota ed è rappresentata da  .

Un linguaggio L non è altro che un sottoinsieme di TUTTE le possibili parole


generabili da un alfabeto  . Tale insieme infinito è chiamato  *. Quindi:
L⊆ *
In particolare ∈ *, ma ∉ +.  + è quindi un sottoinsieme di  * contenente
tutte le parole possibili esclusa la parola vuota.  è il linguaggio vuoto.

Per mezzo del prodotto di giustappozione sono generati casi speciali all'interno di una
stessa parola.
● Prefisso di y è quella stringa x tale che y=xz
● Suffisso di y è quella stringa x tale che y=zx
● Fattore di y e quella stringa x tale che y=zxw

Dati due linguaggi si possono applicare le operazioni booleane: Unione, Intersezione,


Complemento.
Due importanti nozioni basate sul prodotto di giustapposizione tra linguaggi riguardano
inoltre il prodotto, la potenza e la chiusura di Kleene.
● Prodotto: L1∗L 2 ={xy∣x∈ L1 e y∈ L 2 }
● Potenza: L0 ={} oppure L3 =L∗L∗L=L2∗L oppure L n =L∗L n−1
● Chiusura di Kleene: L *= L0 ∪L1 ∪ L2 ∪..... Ln e L += L1 ∪L 2 ∪..... L n
Il prodotto di linguaggi è commutativo solamente su alfabeti unari ossia con un solo
elemento eventualmente moltiplicato per se stesso.

In conclusione:
● L * è un linguaggio ottenuto moltiplicando tra loro in tutti i modi possibili tutte le
parole del linguaggio L unitamente alla parola vuota ottenendo in questo modo un
insieme infinito.
● L + è lo stesso di L * escludendo la parola vuota.

La fattorizzazione di una parola secondo un dato linguaggio è la scomposizione della


stessa in “sottoparole” presenti nel linguaggio.
Dato un linguaggio L += {0,10} e una parola 0010100 , la sua fattorizzazione è:
0∗0∗10∗10∗0
In questo esempio in particolare la fattorizzazione è unica, nel senso che non è
possibile fattorizzare la parola 0010100 in altro modo. In questo caso abbiamo
dunque trovato un codice.
● Un codice ha dunque la proprietà esistenziale di univoca decifrabilità.

Formalmente:
Un linguaggio L in cui ogni parola di L + è decomponibile in un unico modo come
prodotto di parole di L è detto codice.

● Ogni linguaggio prefisso è anche un codice. Viceversa non vale che ogni codice è
anche prefisso. Questo semplicemente perchè non avendo alcuna parola composta
da altre parole, il modo per decomporla è unico.
● Ogni linguaggio con parole di lunghezza uguale è sempre un codice prefisso. Questo
perchè ogni parola ha una sua composizione specifica diversa una dall'altra
assicurata dal fatto che ogni parola ha sempre lunghezza n .
Rappresentazioni
I linguaggi L possono essere descritti e quindi rappresentati in due modi.

1. Se il linguaggio è finito, elencando tutti gli elementi


2. Altrimenti in modo intensivo per mezzo di una proprietà P w del linguaggio
stesso tale che L={w∣P w=TRUE } .

Ci sono due sistemi di rappresentazione intensiva: il sistema riconoscitivo e quello


generativo. I linguaggi possono ammettere entrambi o solamente uno di essi.
Riconoscitore
Il sistema riconoscitivo funziona in modo da riconoscere se una parola w appartiene
o no ad un linguaggio L per mezzo di un algoritmo chiamato riconoscitore che riceve
in input la parola w e ritorna 1 se w∈ L e 0 se w∉ L .
Non tutti i linguaggi ammettono un riconoscitore. Un linguaggio che ammette un
riconoscitore è definito ricorsivo o decidibile.
Ci sono due classi di linguaggio ricorsivi, chiamate linguaggi regolari e linguaggi
acontestuali. Queste classi ammettono un riconoscitore chiamato rispettivamente
automa a stati finiti e automa a pila.

Come abbiamo detto non tutti i linguaggi ammettono un riconoscitore. Questi linguaggi
vengono chiamati ricorsivamente numerabili. Sono classificati ricorsivamente
numerabili i linguaggi che restituiscono 1 se w ∈ L e vanno in loop se w∉ L .
● I linguaggi ricorsivi sono un sottoinsieme dei linguaggi ricorsivamente numerabili.
Questo perchè dato un riconoscitore per un linguaggio ricorsivo è facile, una volta
ricevuto l'output relativo al caso w∉ L , costruire una procedura che entra in loop.
● Il contrario non può essere, perchè è impossibile capire quando un algoritmo è
entrato in loop.
Generatore
Il secondo modo per descrivere un linguaggio è il sistema generativo. Questo sistema
si basa sul concetto di calcolo logico. Un calcolo logico non riconosce se w∈ L , ma ne
ricerca la dimostrazione! Quindi un calcolo logico V  x , d  riceve in input un parola
x , una dimostrazione d e ritorna in output 1 se w∈ L e 0 se w∉ L . Ossia in altre
parole restituisce 1 se la dimostrazione d permette di affermare che la frase/parola
x è vera.

Il calcolo logico ha tre proprietà fondamentali:


● La dimostrazione d verifica in un tempo finito se è o no la dimostrazione di una
frase f .
● Se d è la dimostrazione di f allora f deve essere una affermazione vera
(correttezza del calcolo logico) ossia f ∈ L .
● Se f è un'affermazione vera allora deve esserci una sua dimostrazione
(completezza del calcolo logico) ossia ∃ d  f ∈ L .

Formalmente un calcolo logico è una funzione V : ∗xU ∗ {0,1} . Il calcolo logico


lavora di conseguenza nel campo di  * e in quello di U * che non è altro che
l'insieme infinito delle possibili dimostrazioni.

Vediamo adesso come mai i linguaggi ricorsivamente numerabili ammettono sempre un


calcolo logico corretto e completo. Prendiamo la funzione:

function L (x)
{
n=1
while (V(x,d)=0)
n = (n+1)
return (1)
}

Nel caso w∈ L questa la funzione termina, altrimenti va il loop. Siamo quindi


pienamente nel caso di linguaggi ricorsivamente numerabili. Dal momento che poi
questa funzione o restituisce 1 oppure non termina, il calcolo logico risulta essere
corretto e completo perchè V  x , d =1 sempre se w∈ L .
Dato un linguaggio L , se un calcolo logico è corretto e completo per L allora per
forza di cose L∈{x∣∃ d V  x , d =1} .
Non solamente i linguaggi ricorsivamente numerabili ammettono calcoli logici, però un
linguaggio NON ricorsivamente numerabile ammetterà un calcolo logico al più corretto
ma incompleto.

Se noi ad esempio forziamo l'uscita dal ciclo while ad un certo numero n (questo per
rispettare il fatto di lavorare su linguaggi non ricorsivamente numerabili e quindi che
non ammettono un loop) avremo un calcolo logico completo perchè effettivamente
w ∈ L , ma incompleto in quanto non abbiamo avuto abbastanza tempo da trovare una
dimostrazione tale che V  x , d =1 .
Grammatiche
Un sistema generativo sono le grammatiche. Una grammatica è una quadrupla
〈 ,Q , P , S 〉
●  e Q sono due alfabeti rispettivamente di simboli terminali e di metasimboli.
● P è l'insieme di regole di produzione
● S è un simbolo chiamato assioma
Innanzi tutto a differenza dei simboli, i metasimboli o simboli NON terminali sono
espressioni atte alla generazione del linguaggio e non sono parole appartenenti al
linguaggio.
Le regole di produzione sono essenziali per la creazione di un linguaggio in quanto
dettano come è possibile articolare un insieme di simboli e metasimboli al fine di
ottere un linguaggio più completo e complesso.
In generale le regole si produzione sono dichiarate nel seguente modo:

con  e  due parole di simboli e/o metasimboli tali che
∈ ∪Q ∗e ∈ ∪Q ∗.
Infine l'assioma non è altro che il simbolo o il metasimbolo di partenza.
La rappresentazione
 ⇒G 
indica poi la possibilità di derivare un simbolo  da un simbolo  secondo la
grammatica G in un sol passaggio. Mentre
 ⇒G∗
indica che si può derivare  da  con un numero finito di derivazioni.
Proprio grazie alla derivazione possiamo notare come le grammatiche siano calcoli
logici in cui la dimostrazione d è la derivazione stessa.
Per ipotesi un linguaggio L è generato da una grammatica solamente se L è
ricorsivamente numerabile, ma allora L è generabile da una grammatica solamente
se per L esiste un calcolo logico corretto e completo.
Un linguaggio LG  generato da una grammatica di tipo G è l'insieme delle parole
sull'alfabeto  derivabili dall'assioma S , ossia:
LG ={w∣w ∈∗e S ⇒G∗w }
Con grammatiche equivalenti si intende il caso in cui date due grammatiche G1 e
G2 :
LG1= LG2
Classificazione
Le regole di produzione sono usate per la classificazione delle grammatiche. A seconda
del tipo di regola esiste un tipo di grammatica.

Una classificazione importante è l'omonima proposta da Chomsky.


1. Grammatica di tipo 0: qualsiasi tipo di regole di produzione
2. Grammatica di tipo 1: regole di produzione del tipo    con l l  . E'
possibile la regola S   purchè S sia l'assioma, ed S non compaia in nessun altra
regola.
3. Grammatica di tipo 2: regole di produzione del tipo A   con A rigorosamente
un metasimbolo
4. Grammatica di tipo 3: regole di produzione del tipo A   B , A   oppure
A   . Dove A e B sono metasimboli e  un simbolo.

Una grammatica G genera un linguaggio LG  e per questo motivo, la


classificazione delle grammatiche genera a sua volta la classificazione dei linguaggi.

Un linguaggio L è di tipo k (con K = 0,1,2,3) se esiste una grammatica di tipo k che lo


ha generato, ossia tale che L= LG  .
Si indica con Rk la classe di linguaggi di tipo k. In particolare, secondo la
classificazione di Chomsky, le classi di linguaggi formano tra loro sottoinsiemi propri
nel seguente modo:
R3⊆R2⊆R1⊆R0

Riprendendo alcune definizioni fatte precedentemente sui linguaggi si può dire che:
● R0 sono tutti i linguaggi ricorsivamente numerabili.
● R1 sono tutti i linguaggi contenstuali.
● R2 sono tutti i linguaggi acontenstuali.
● R3 sono tutti i linguaggi regolari.

Possiamo notare subito come i linguaggi regolari e acontestuali, introdotti per il


sistema riconoscitivo, siano linguaggi particolari che ammettono anche un sistema
generativo.
Parlando in generale non è sempre detto che un linguaggio sia generato da una sola
grammatica. Ci possono essere linguaggi generati da classi di grammatiche più
generali. Un esempio di ciò sono le grammatiche di tipo 3 e la grammatica lineare a
destra.

Una grammatica lineare a destra ha regole di produzione del tipo A  x B , A  y


con x , y parole. Una grammatica di tipo 3 è anche una lineare a destra, ma il
viceversa non è sempre detto.
Dato infatti una grammatica lineare a destra con regole di produzione
A   1  2  3 ... m B e A   1  2  3 ... m , possiamo costruire una grammatica
equivalente introducendo nuovi metasimboli M 1, M 2, M 3 ... M m e sostituendo con
nuove regole del tipo A   1  2  3 ...  m B .
Alla fine otterremo una grammatica con regole di produzione A   B , A   ,
A   e A  B . A causa dell'ultima regola la grammatica non è di tipo 3, ma per ogni
derivazione F ⇒G∗H possiamo considerare tutte quelle per cui F ⇒G∗A e B ⇒G∗H
aggiungendo la regola F   H . Questo perchè è possibile nella grammatica una
derivazione F ⇒G∗A⇒G  B ⇒G∗H che equivale a dire appunto F   H .
Aggiungiungiamo poi la regola F   / per ogni F per cui F ⇒G∗A⇒G  / . A
questo punto abbiamo una grammatica di tipo 3.

Altri esempi di equivalenza sono dati dalla possibilità di generare un linguaggio di tipo
3 da una grammatica di tipo 3 contenente però solamente regole A   B e A   .
Ciò si ottiene introducendo un nuovo metasimbolo M e una nuova regola M   e
sostituendo la regola A   con A   M . Quindi:
● Se L è di tipo 3 allora è generato da una grammatica di tipo 3 con regole del tipo
A B e A .
Il contrario, ossia generare una grammatica equivalente con regole solamente del tipo
A   B e A   non è sempre detto perchè in generale date queste regole allora
∉ L . Però:
● Se L è di tipo 3 con ∈ L , allora L=L ' ∪{} dove L ' è generato da una
grammatica di tipo 3 con regole del tipo A   B e A   .
Automi a stati
Un automa a stati A è un sistema
A=〈  ,Q ,  , q0 , F 〉
dove:
● Q è un insieme di stati
●  è un alfabeto finito
●  è la funzione di transizione
● q0 è lo stato iniziale
● F è l'insime degli stati finali
Se l'insieme Q è finito l'automa prende il nome di automa a stati finiti.
Un esperimento su questo sistema avviene in due fasi:
1. Viene dato come input un messaggio composta da una parola w∈ *.
2. Si osserva il valore di ouput se questo è 1 allora la parola w è accettata altrimenti
scartata.
Un sistema simile è conoscibile solamente per mezzo di esperimenti, ossia il
comportamento del sistema è descritto dalle parole accettate. Questo sistema è
quindi un riconoscitore di linguaggio.
Più nel particolare un sistema di questo tipo è composta da un numero finito di stati:
1. Ad ogni istante il sistema si trova su uno stato specifico q∈Q . Per modificare il
suo stato si deve mettere in ingresso un nuovo messaggio in modo tale da poter
associare il sistema ad un nuovo stato, ossia ad uno stato futuro o prossimo. La
legge che descrive questa procedura prende il nome di funzione di transizione
 :  x Q Q . Per esempio se il sistema è nello stato q ed arriva un messaggio 
la sua funzione sarà q ,   .
2. Inizialmente il sistema si trova sempre in uno stato iniziale q0 ∈Q .
3. L'output del sistema è invece rappresentato dalla funzione di uscita  : Q {0,1} e
se il sistema finisce in uno stato q allora q . Il sistema inoltre può uscire
solamente da uno stato finale. La totalità di questo stati forma l'insieme degli stati
finali F ={q∣q=1} .
Il linguaggio L A riconosciuto dall'automa A
L A= { w∣w∈∗e ∗q0 , w }={w∣w∈∗e ∗q0 , w=1}
Un automa a stati può essere rappresentato graficamente con un diagramma per
mezzo di cerchi (che rappresentano gli stati) e archi (che rappresentano le possibili
transizioni tra stato e stato). Lo stato iniziale è rappresentato con una freccia in
ingresso, mentre quello finale con un cerchio concentrico.
Esempio: A=〈 S ,Q , , q1 , F 〉
● S = {a.b}
● Q = {q1,q2}
● F = {q2}
●  : {a , b}x {q1 , q2} {q1 , q2} descritto dalla tabella:
d a b
q1 q1 q2
q2 q2 q1

Il diagramma a stati sarà:

a a

b
q1 q2
b

In questo esempio possiamo notare come ogni parola dell'alfabeto induca un


cambiamento di stato. In particolare le parole accettare saranno quelle che portano
da dallo stato iniziale allo stato finale.
Osservabilità
Dato un automa a stati si dice che un suo stato q è osservabile se q=q0 , w . Ossia
se è possibile che l'automa raggiunga lo stato q avendo in ingresso un input w .

a a

b
q1 q2
b

q3

Nell'esempio sopra lo stato q3 è inosservabile! Gli stati inosservabili sono tanto


irrilevanti da poter essere soppressi rendendo l'automa osservabile. Infatti se tutti gli
stati dell'automa sono osservabili, allora l'automa è definito anch'esso osservabile.
Indistinguibilità
Dati due stati q1 e q2 , si definiscono indistinguibili – si scrive q1≈q2 - se
q1 , w=q2 , w
Ossia se, dato come ingresso una parola w , gli stati q1 e q2 portano entrambi ad
uno stesso stato q3 , allora non è possibile empiricamente conoscere il
comportamento dell'automa e quindi gli stati si definiscono indistinguibili.
Al contrario, quando
q1 , w ≠q2 , w
sono distinguibili!

L'indistinguibilità gode delle proprietà:


● riflessiva: q1≈q1
● simmetrica: q1≈q1 q2≈q1
● transitiva: q1≈q2 e q2≈q1⇒ q1≈q3

Dato un automa indistinguibile è possibile costruire un automa distinguibile. Infatti gli


stati indistinguibili, tra loro, formano classi di equivalenza. Per esempio se q1≈q2
esiste una classe di equivalenza [q1]≈. La classe comprende q1 , q2 e tutti gli
eventuali stati equivalenti.
In particolare una generica classe di equivalenza [q1]≈ , può essere vista come un
singolo stato. Prendendo quindi ogni classe di equivalenza e usandole come singoli
stati otterremo un automa i cui stati sono tutti distinguibili tra loro ottenendo di
conseguenza un automa distinguibile.
Sintesi di automa
Gli automi pienamente indistinguibili sono quelli più vicini al concetto di automa
gigante GL in cui ogni parola ha il suo stato finale. In un automa gigante diverse parole
corrispondono a diversi stati. In particolare data una parola w il suo stato si indicherà
con [ w ] .
Inoltre per quanto riguarda un automa gigante, l'insieme dei suoi stati sarà infinito
perchè le parole w∈ * sono infinite.
Un automa minimo GM è invece l'esatto contrario ed è un automa distinguibile avente
ovviamente più stati per singola parola.

● In generale dato un automa A che riconosce un linguaggio L , il relativo automa


minimo ha un insieme di stati non più numeroso di quello di A .

Per raggiungere l'automa minimo da un automa gigante è necessario effettuare


l'operazione vista nel precedente paragrafo, ossia identificare gli stati indistinguibili
per poi sopprimerli uno ad uno.

Per costruire un GM da un GL si devono eseguire le seguenti operazioni:


● Considerare un GL
● Scorrere il GL dalla radice  , visitando l'albero in ampiezza. Quando si arriva ad un
generico nodo [ w  ] , cui arriva un arco etichettato  dal nodo [ w ] , se questo è
indistinguibile da un nodo [ x ] precedentemente analizzato allora collegare il nodo
[ w ] ad [ x ] , con un arco  e sopprimere il nodo [ w  ] .
● Mantenere il nodo iniziale  e i nodi finali [ x ] se x ∈ L .
ASF e G3
Gli automi a stati finiti sono riconoscitori per grammatiche di tipo 3. Vediamo adesso
come da una grammatica 3 sia possibile costruire un automa a stati finiti e il
viceversa.

● Per un linguaggio L riconosciuto da un automa a stati finiti, esiste una grammatica


G di tipo di 3 tale che L= LG  .

avendo l'automa:
A=〈Q ,  ,  , q0 , F 〉
si costruisce la grammatica G :
G=〈 S , Q , P , q0 〉
in modo tale che:
● L'insieme dei metasimboli coincide con gli stati dell'automa
● L'assioma è lo stato iniziale
● qk  qj è una regola di produzione se qj=qk , w
● qk   è una regola di produzione se qk ∈ F

Esempio:

q0 a q1 a q2
a

Il linguaggio riconosciuto da questo automa è anche generabile dalla grammatica con


assioma q0 e con le seguenti regole di produzione:

● q0  aq1
● q1 aq2
● q2  aq1
● q2  
Al contrario possiamo dire che data una grammatica di tipo 3 è possibile costruire un
automa a stati finiti.

Prendiamo una grammatica di tipo 3


G=〈 S ,Q , P , S 〉
che supponiamo contenga tali regole:
● A B
● A 

Possiamo costruire un grafo associato con:


● Metasimboli in Q come vertici del grafo.
● Un arco etichettato  tra A e B se esiste una regola A B .
● Assioma S .
● Vertice finale A se esiste una regola A .

Esempio:
Abbiamo questa grammatica:
● q0→aq0
● q0→aq1
● q1→bq0
● q0→e

Costruiamo il nostro grafo associato:

a
b q1
q1
q0 a

Interpretando i metasimboli come stati ci accorgiamo che l'automa associato non è un


automa a stati finiti. Infatti ci sono due archi etichettati a e quindi non esiste più un
unico stato prossimo.

Un automa di questo tipo esce dal campo deteministico ed entra in quello


probabilistico prendendo il nome di automa non deterministico.
Formalmente:
● Un automa a stati finiti non determinitico A è un sistema
A=〈Q ,  , R , q0 , F 〉
dove Q è un insieme finito di stati,  è un alfabeto finito, R : Q x  x Q  0,1 è la
relazione di transizione, q0∈Q è lo stato iniziale, F ⊆Q è l'insieme di stati finali.

R : Q x  x Q  0,1 è rappresentata dalla relazione


R ' ⊆q ,  , q '  sse Rq ,  , q ' =1
ossia R ' è sottoinsieme di Q x xQ con elementi solamente quelli con
Rq ,  , q ' =1 ossia quelli aventi arco  .

Detto ciò R : Q x  x Q  0,1 è


R ' ' : Qx   P Q 
con P Q l'insieme delle parti di Q e
R ' ' :q , ={q '∣Rq , , q ' =1}
ossia, dato un messaggio  , la funzione di transizione R ' ' descrive un passaggio ad
uno stato appartentente al sottoinsieme sopradescritto R ' .

In altre parole un grafo non deterministico può essere rappresentato da un automa


etichettato i cui vertici sono gli stati q ed esiste un arco tra q e q ' se e solo se
Rq ,  , q ' =1 .

Per esempio se Rq1 , , q2=1 allora q2=q1 ,   . In tal caso la relazione R è


equivalente alla funzione per l'automa a stati finiti  :  xQ Q .

● Una qualsiasi parola w è riconosciuta da un automa a stati non deterministico


solamente se questa parola induce un qualsiasi cammino dallo stato iniziale ad uno
stato finale.
Per ogni linguaggio L riconosciuto da un automa a stati non deterministico esiste un
automa a stati finiti che lo riconosce.

Per ricavare un automa deterministico da un non deterministico prima di tutto


prendiamo in considerazione l'insieme degli stati dell'automa non deterministico.
Nell'esempio precedente abbiamo K =q0 , q1 . L'insieme dell parti rappresenta il
numero di stati del nuovo automa deterministico in relazione agli stati di quello non
deterministico. In questo caso è P  K = , q0 , q1 , q0q1 .

L'associazione tra stati è dettata dalle regole di produzione della grammatica. Per
esempio si ha q0→aq0 e q0→aq1. Queste due regole vengono tradotte nel nuovo
automa deterministico con la regola q0→aq0q1 ossia dallo stato q0 di P(K) si passa a
quello q0q1.

In conclusione a questo capitolo:


1. L è generato da una grammatica di tipo 3
2. L è riconosciuto da un automa a stati finiti

sono due affermazioni equivalenti tale che 1 implica 2 e 2 implica 1.


Espressioni regolari
I linguaggi regolari oltre ad essere generati da grammatiche di tipo 3 e riconosciuti da
automi a stati finiti, possono essere denotati da espressioni regolari.

Dato un alfabeto  le espressioni regolari sono definite induttivamente:


● ∅ ,  , con  ∈ sono espressioni regolari
● Se p , q sono espressioni regolari allora  pq , p∗q , p * sono espressioni
regolari

Ad ogni espressione regolare è associato – e si dice denota - un linguaggio, ad


esempio:
● ∅ denota il linguaggio vuoto
●  denota il linguaggio {}
●  denota il linguaggio { }
Con p , q che denotano L1 , L2 allora:
●  pq denota L1∪L2
●  p∗q denota L1∗L2
● p * denota L *

Le espressioni regolari denotano linguaggi in modo composizionale ossia avendo come


linguaggi-base ∅ , {} , { } si possono denotare linguaggi complessi sottoponendoli
alle operazioni di unione, prodotto e chiusura.
In particolare i linguaggi regolari sono tutti e soli i linguaggi riconosciuti da automi a
stati finiti.
Teorema di Kleene
● L è denotato da una espressione regolare
● L è riconosciuto da un automa a stati finiti
dicono la stessa cosa!
Per dimostrarlo prima di tutto dobbiamo sapere che essendo i linguaggi “denotati”
composizionali, le dimostrazioni della generica proprietà P dei linguaggi per mezzo
delle espressioni regolari avvengono in modo induttivo, ossia:
1. Essendo ∅ ,  , espressioni regolari che verificano la proprietà P
2. Se i linguaggi A , B verificano P allora A∪B , A∗B , A * verificano P
Quindi per dimostrare che la frase 1 implica 2 dobbiamo provare che:
1. ∅ ,  , sono risciuti da automi a stati finiti
2. Se A , B sono riconosciuti da automi a stati finiti, allora A∪B , A∗B , A * sono
riconosciuti da automi a stati finiti
Per il punto 1 basta vedere che gli automi:

riconoscono rispettivamente ∅ ,  , .
Per il punto 2 supponiamo che A , B siano riconosciuti da automi a stati finiti. Di
conseguenza sono rispettivamente generati, come abbiamo visto, da una grammatica
di tipo 3 del tipo
G ' =〈 S , Q ' , P ' , S ' 〉 e G ' ' =〈 S , Q ' ' , P ' ' , S ' ' 〉
Supponiamo inoltre che le regole di produzione siano del tipo
● qk  qj
● qk  
e che Q ' , Q ' ' siano insiemi disgiunti. Quindi:
● A∪B è verificato perchè generato dalla grammatica G1=〈 S , Q1 , P1 , S1〉 in cui
Q1=Q ' ∪Q ' ' con un nuovo metasimbolo S2 , P1=P ' ∪P ' ' con S1 S ' e
S1  S ' ' come regole nuove aggiuntive.
● A∗B è verificato perchè generato dalla grammatica G2=〈 S , Q2 , P2 , S ' 〉 in cui
Q2=Q '∗Q ' ' con un nuovo metasimbolo S2 , P1=P '∗P ' ' esclusa la regola
q '   con la nuova regola q '  S ' in aggiunta.
● A * è verificato perchè generato dalla grammatica G3=〈 S , Q ' , P3 , S ' 〉 in cui
P3=P ' con l'aggiunta della regola q '  S ' .
Alberi di derivazione
L'albero di derivazione ha le seguenti proprietà:
● Le foglie sono simboli terminali
● I nodi sono metasimboli
● La radice è l'assioma
● Se un nodo è etichettato B e i figli, in ordine, B1 , B2 , B3 ... Bn allora B1... Bn
è una regola di produzione
● Leggendo le foglie in ordine prefisso, la sequenza di etichette forma una parola w

Esempio:
Data la grammatica: G=〈{ , ,, x , y }, {E }, {E  E E / x / y }, E 〉
e una parola  x y  x  ha la seguente derivazione:
E ⇒ g  E E ⇒ g  E E E ⇒ g  xE E ⇒ g  x y E ⇒ g  x y  x 
e il corrispondede albero di derivazione:

Osserviamo che differenti derivazioni possono generare stessi risultati e differenti


derivazioni possono generare stessi alberi!
Si dicono quindi equivalenti le derivazioni che producono lo stesso albero di
derivazione.
E' interessante per questo motivo ottenere un elemento rappresentativo di classi
equivalenti. Una soluzione è la cosidetta derivazione più a sinistra.
● Data una grammatica G=〈  ,Q , P , S 〉 di tipo 2 e una parola w∈ *, se la
derivazione di w da S è ottenuta applicando una regola di produzione al
metasimbolo più di sinsitra, allora viene detta derivazione più a sinistra.
E' interessante osservare come questa particolare derivazione sia semplicemente la
vista in profondità dell'albero di derivazione.
Ambiguità
Dal momento che poi ogni albero di derivazione ha associata una derivazione più a
sinistra e viceversa, ogni derivazione più a sinistra risulta avere una corrispondenza
bunivoca con gli alberi di derivazione!
Data una grammatica G=〈{ , ,, x , y }, {E }, {E  E E / x / y }, E 〉
che genera un linguaggio LG  possiamo interpretare una parola w∈ LG  come
espressione aritmetica.
Per farlo basta assegnare ai simboli terminali un valore oppure un operazione binaria.
Ad esempio data la parola  x y x possiamo associare ad x , y i valori 3, 5
ottenendo 353=11 .
Facendo in questo modo abbiamo associato alla nostra parola un significato!
Per avere un significato valido, dobbiamo avere un unico albero di derivazione per
singola parola! Ma come abbiamo visto oltre ad esserci varie possibili derivazioni,
potrebbero esserci vari possibili alberi di derivazione!
In questo caso il nostro significato della parola perde senso e diventa quindi ambiguo!
Esempio, data la grammatica:
G=〈{¿ ,, x , y }, {E }, {E  E E / E∗E / x / y }, E 〉
Sono due alberi di derivazione che generano x∗y∗x in una grammatica appropriata.
Quindi:
● Una grammatica G di tipo 2 è detta ambigua se esiste una parola w∈ LG  che
ammette due alberi di derivazione. Una grammatica G di tipo 2 è detto non
ambigua se la parola w∈ LG  ammette solo un albero di derivazione.

In generale linguaggi generati da grammatiche di tipo 3 ambigue sono sempre


riconosciuti da grammatiche equivalenti non ambigue. Per ottenere la grammatica non
ambigua è necessario ricavare il riconoscitore per il linguaggio. La grammatica
derivata dal riconoscitore sarà non ambigua.
Ci sono casi inoltre di linguaggi che richiedono solo grammatiche di tipo 2 ambigue per
essere generati. Questo tipo di linguaggio è chiamato inerentemente ambiguo.
Forme normali
Due importanti sottoclassi della grammatica di tipo due sono le seguenti:
● Data una grammatica G di tipo 2, è detta forma normale di Chomsky se le regole
di produzione sono del tipo A BC oppure A x , con ABC metasimboli e x
simbolo terminale.
● Data una grammatica G di tipo 2, è detta forma normale di Greibach se le sue
regole di produzione sono del tipo A xW con W una parola di metasimboli
(eventualmente vuota) e x un simbolo terminale.
Per ogni linguaggio L libero acontestuale privo della parola vuota esiste una
grammatica G ' in forma normale di Chomsky e una G ' ' in forma normale di
Greibach generanti il linguaggio L .
Pumping Lemma
Dato che una derivazione di una grammatica in forma normale di Chomsky genera un
albero binario, sappiamo a priori che l'albero binario in questione, avendo n foglie,
ha anche un'altezza almeno log 2 n .
Da questa certezza otteniamo una condizione necessaria chiamata pumping lemma
per la quale possiamo definire un linguaggio acontestuale o no.

Per ogni linguaggio acontestuale L esiste una costante H tale che ogni parola z ∈ L
con lunghezza ∣z∣H può essere decomposta nella forma z=uvwxy tale che:
1. ∣vx∣1 ossia almeno una parola tra v e x è differente da 
2. ∣vwx∣H
3. per ogni k 0 vale che u v k w x k y ∈ L
Supponiamo di avere una grammatica G in forma normale di Chomsky che genera un
linguaggio L .
h è il numero di metasimoli in G . Abbiamo poi una parola z ∈ L tale che ∣z∣H
con H =2h1 .
Come abbiamo visto, c'è per z un albero di derivazione con H foglie e altezza
log 2  H  .
Prendiamo adesso in considerazione il cammino C più lungo e discendente dalla
radice ad una foglia. C sarà per forza di cose h1=log 2  H  .
Dal momento che G ha h metasimboli, in C sarà per forza presente una ripetizione
di un metasimbolo qualsiasi A !
Chiamiamo w la parola composta dalle foglie del sottoalbero del secondo
metasimbolo A e vwx la parola composta dalle foglie del primo metasimbolo A .
Abbiamo così ottenuto z=uvwxy con u , y opportune parole.
Risulta inoltre che:
1. ∣vx∣1 altrimenti le radici sei sottoalberi etichettati A coninciderebbero.
2. ∣vwx∣H in quanto il sottoalbero con radice nel primo metasimbolo A ha altezza
al più h1 e di conseguenza possiede al più H =2h1 foglie.
3. per ogni k 0 vale che u v k w x k y ∈ L . Dal momento che:
S ⇒ g∗uAy , A⇒ g∗vAx , A⇒ g∗w
allora:
S ⇒ g∗uAy ⇒ g∗uvAxy ⇒ g∗uv 2 Ax 2 y ⇒ g∗u v k A x k y ⇒ g∗u v k w x k y
Automi a pila
I linguaggi acontestuali non possono essere riconosciuti da automi a stati finiti dal
momento che non possiedono una memoria. Un linguaggio acontestuale:

L={a n b n∣n≥1}
non può essere riconosciuto da un automa a stati finiti in quanto non gli è possibile
contare il numero di a per confrontarlo al numero di b .
E' necessario quindi una memoria teoricamente infinita... la memoria a pila o stack.
Per mezzo di una memoria a pila è possibile memorizzare una qualsiasi parola su un
alfabeto K e implementare le seguenti operazioni:
● Controllare se una parola è vuota: ISEMPY  z =0,1 .
● Leggere il primo simbolo: TOP  z  .
● Cancellare il primo simbolo: POP  z  .
● Aggiungere un simbolo in testa: PUSH a , z  .
Per capire il funzionamento dell'automa a pila prendiamo un linguaggio L
acontestuale generato da una grammatica in forma normale di Greibach e applicando
una derivazione più a sinistra.
Applicando una regola X1 aW ad una parola x1x2x3X1X2X3 otteniamo
x1x2x3aWX2X3 . Notare che per effettuare una simile operazione basta:
1. POP  X1
2. PUSH W 
Per applicare una regola qualunque A aV serve però prima il riconoscimento del
metasimbolo. Questa operazione è effettuata tramite TOP . Infatti
TOP  X1X2X3= X1 . Adesso si controlla se X1= A e si applica o meno la regola.

Informalmente un automa a pila funziona nel seguente modo:


La parola è contenuta in un nastro. Il nastro è letto simbolo per simbolo da un
riconoscitore e la memoria a pila contiene una parola di metasimboli.
Esempio:
pila = X1 X2 X3
riconoscitore = xk
l'automa controlla se esiste una regola del tipo X1 xkW e se la trova cancella con
POP  X1 e immette con PUSH W  nella memoria a pila.
Un automa a pila è di conseguenza generalmente non deterministico perchè in un
insieme di regole di produzione non è possibile determinare con sicurezza quale venga
adoperata e in che caso.
Più formalmente: Un automa a pila è un sistema
= 〈  , K ,  , S 〉
dove:
●  è un alfabeto di simboli terminali
● K è un alfabeto disgiunto da 
● S è un elemento di  posizionato come simbolo iniziale dentro lo stack
●  è una funzione da  x K ai sottoinsiemi finiti di  *
a , A={W1 , W2 .... , Wn } funziona in modo che letto un simbolo a e avendo in
cima alla pila il metasimbolo A , si cancella A e si inserisce un metasimbolo Wk
dell'insieme.

Un linguaggio è acontestuale se e solo se è risconosciuto da un automa a pila!

Una parola è riconosciuta da un automa a pila se la sua memoria stack alla fine del
riconoscimento risulta essere vuota!