LINGUAGENS FORMAIS E AUTÔMATOS
O objetivo deste curso é formalizar a idéia de linguagem e definir os tipos de sintaxe e semântica. Para
cada sintaxe, analisamos autômatos, que são abstrações de algoritmos. Dentre as classes de linguagens
temos:
Regulares ⊆ Livres de contexto ⊆ Sensíveis ao contexto ⊆ Recursivas ⊆ Recursivamente
enumeráveis
O curso de Linguagens Formais e Autômatos se ocupa principalmente dos primeiros, enquanto os dois
últimos são objetivo de um curso de Computabilidade.
Uma linguagem comum é um conjunto de três aspectos:
• Aspecto léxico – Define os símbolos (signos) que compõe a linguagem.
• Aspecto Sintático – Define regras para símbolos que indicam que seqüências de símbolos são
aceitas.
• Aspecto Semântico – Dá o significado das seqüências de símbolos.
Nosso curso se concentra nos dois primeiros aspectos, que são vitais para a construção de compiladores.
Podemos definir linguagem como:
1. Linguagem é um conjunto de sentenças.
2. Sentença sobre um vocabulário é uma seqüência finita de símbolos do vocabulário.
3. Vocabulário é um conjunto finito de símbolos.
Por enquanto, definimos uma linguagem só definindo seu aspecto léxico. Assim, se queremos definir uma
linguagem para uma calculadora, definimos o alfabeto (vocabulário):
∑
= {+ , - , * , / , NUM}
Sentenças sobre esse vocabulário são:
W1 = NUM * NUM + NUM
W2 = + * NUM
Uma linguagem é um subconjunto do conjunto das sentenças. Podemos considerar somente:
L = { NUM , NUM + NUM , NUM * NUM ...}
A linguagem pode ser definida de forma recursiva:
NUM ∈ L
α β ∈ L ⇒ α + β , α - β , α * β , α / β ∈L
E
E
E
E
E
→
→
→
→
→
NUM
E+E
E-E
E*E
E/E
Assim também:
E → E + E → E * E + E → E * E + NUM → E * NUM + NUM → NUM * NUM + NUM
Definimos sobre o conjunto de sentenças a operação de concatenação. Por exemplo:
W1 = ++*
W2 = NUM
W1 W2 = ++*NUM
Dado um alfabeto (vocabulário)
∑
, definimos
∑
*
como o conjunto de todas possíveis sentenças,
incluindo ε que representa a sentença vazia. Dessa forma, toda linguagem é:
L⊆ ∑ *
Definimos também
∑
+
= ∑ */{ε } , ou seja, o conjunto de todas possíveis sentenças não vazias. No
nosso exemplo do vocabulário da calculadora:
∑ * = {ε , +, −, +,*, /, +−, +*, ++, + NUM , −−,...}
Em seguida, vamos definir o que é uma gramática, uma linguagem gerada por uma gramática. A cada tipo
de linguagem (definida pelo seu tipo de gramática) e a cada um dele vamos associar um autômato.
Linguagem
Regulares
Livres de Contexto
Sensíveis ao Contexto
Sem Restrição
Autômato
Autômato Finito
Autômato com pilha
Máquina de Turing Linearmente Limitada
Máquina de Turing
Definimos uma gramática G como uma 4-tupla G = 〈
∑
∑
, N , P , S 〉 , onde:
= Alfabeto de símbolos terminais.
N = Conjunto de símbolos não terminais.
P = Conjunto de regras de produção, da forma: α → β , α , β ∈ (∑ ∪ N )* e α tem ao menos um símbolo
de N.
S = Símbolo inicial. S ∈ N.
Para a linguagem que definimos, uma gramática adequada seria:
∑
= {+ , - , * , / , NUM}.
N = {E}
P ={ E → NUM , E → E + E , E → E – E , E → E * E , E → E / E}
S=E
Dada uma gramática, definimos em (∑ ∪ N )* a relação binária __________ em um passo (⇒G ) como:
⎧ w1 = γ 1.α .γ 2
⎪
( w1 ⇒G w2) ⇔ ⎨ w2 = γ 1.β .γ 2
⎪(α → β ) ∈ P
⎩
Onde γ 1, γ 2, α , β ∈ (∑ ∪ N )* . Seja (⇒G ) * o fecho reflexivo de (⇒G ) . Assim definimos a linguagem
gerada pela gramática G como
L(G) = {w ∈ ∑ */ s ⇒G *w}
Por exemplo, a linguagem composta pelas seqüências binárias ε , 0, 1, 00, 01, 10, 11, 000, 001,... pode
ser gerada pela seguinte gramática:
∑ = {0,1}
N = {S }
P = {S → S1, S → S 0, S → 0, S → 1}
S=S
Assim, 010 ∈ L(G ) .
Um problema é, dada um alfabeto ∑ e uma gramática G, como decidir se uma sentença w ∈ ∑ * pertence
ou não à linguagem L(G). Isso normalmente é feito usando autômatos. Analisemos alguns antes de definir
formalmente o que é um autômato sobre ∑ ={0, 1}.
1. O seguinte autômato reconhece todas as sentenças com um número par de zeros:
Onde
representa um estado e
“máquina” pode parar.
um estado final, ou seja, um ponto em que o funcionamento da
2. Seqüências de 0’s e 1’s que têm um número par de 0’s ou um número ímpar de 1’s.
3. Seqüências que terminam em 01
4. Seqüências que tem o comprimento múltiplo de 3.
5. O antepenúltimo caractere é 1. Para organizar o raciocínio, vamos chamar os estados de qa ,b ,c , onde
a,b,c ∈ {x, 1}, onde x representa qualquer coisa diferente de 1, seja 0 ou nenhum resultado.
Definição: Um autômato finito determinístico é uma 5-upla M = {Q, ∑, δ , q0 , F } onde:
•
•
•
Q é o conjunto finito de estados
∑ é o alfabeto de entrada
q0 ∈ Q é o estado inicial
•
F ⊆ Q é o conjunto de estados finais.
No exemplo das linguagens que terminam em 0 e 1 temos:
∑ = {0,1}
Q = {q0 , q1 , q2 }
F = {q2 }
A função δ : Q × ∑ → Q é dada pela seguinte tabela:
⇒ q0
0
q1
1
q0
q1
q1
q2
* q2
q1
q0
^
Definamos recursivamente a função δ : Q × ∑* → Q sobre o conjunto das seqüências como:
^
δ ( q, ε ) = q
^
^
δ (q, aω ) = δ (δ (q, a), ω ), onde a ∈ ∑, ω ∈ ∑ *
Assim, para o exemplo que estávamos discutindo:
^
^
^
δ (q0 ,101) = δ (δ (q0 ,1), 01) = δ (q0 , 01) =
^
^
^
^
= δ (δ (q0 , 0),1) = δ (q1 ,1) =
= δ (δ (q1 ,1), ε ) = δ (q2 , ε ) = q2
^
Na prática, δ (q, ω ) representa o estado alcançado a partir de q pelo caminho ω . Assim, definimos o
conjunto de linguagens reconhecidas por M como:
^
T ( M ) = {ω ∈ ∑ *, δ (q0 , ω ) ∈ F }
Exercícios
Fazer um autômato finito que reconheça
i. ω ∈ {0,1}*, tal que termina em 01 ou tem um número par de 0’s.
ii. ω ∈ {0,1}*, tal que ω é a representação binária de um número múltiplo de 5.
iii. ω ∈ {0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,x}, tal que ω é um inteiro positivo na linguagem C. Por
exemplo:
235 – inteiro
075 – octal
0x7FF – hexadecimal
O problema i tem uma solução com somente 4 estados que é:
q0 : número par de 0’s
qix 0 : número ímpar de 0’s terminando em 0
qi 01 : número ímpar de 0’s terminando em 01
qi11 : número ímpar de 0’s terminando em 11
Uma pergunta natural seria qual a gramática reconhecida pelo autômato finito do problema iii, ou seja,
que linguagem descreve os números inteiros da linguagem C? Dando nome aos estados temos:
S → 0Z
S → D ec * D
D → D ec D
Isso nos dá uma receita para a construção de uma gramática que gera a linguagem
reconhecida por um autômato finito. Isso nos dá uma prova construtiva para:
Teorema: Dado um autômato finito M = {Q, ∑, δ , q0 , F } , construo um não
D → ε
Z → xX
terminal N q para cada q e considero a gramática G = 〈
X → H ex H
H → H ex H
N = {N q / q ∈ Q} e
H → ε
Z → O ct O
~
∑
, N , P , S 〉 , onde
~
P = {N q → aN q / δ (q, a ) = q} ∪ {N q → ε / q ∈ F } .
Assim, nestas condições L(G) = T(M).
O → O ct O
O → ε
Classificação das gramáticas
Gramática Regular
Dizemos que uma gramática é regular quando todos os elementos de P são da forma:
A→a
A → aB
A →ε
para A ∈ ∑ e A,B ∈ N
Gramática Livre de Contexto
Dizemos que uma gramática é livre de contexto se todas as regras são da forma:
A → ω , com A ∈ N e ω ∈ ( N ∪ ∑) *
Gramática Sensível ao Contexto
Dizemos que uma gramática é sensível ao contexto quando todos os elementos de P são da forma:
ω1 → ω2 , com ω1 , ω2 ∈ ( N ∪ ∑) *
e ω1 ≤ ω2
Note que, por essa definição, Regulares são Livres de contexto, mas não
são necessariamente sensíveis ao Contexto, pois elas podem ter a regra
A → ε , onde 1= A > ε = 0 . Para corrigir esse defeito, redefinimos
gramática sensível ao contexto como gramáticas cujas sentenças têm a
forma:
ω1 → ω2 , ω1 ≤ ω2 ou
S → ε , onde S é um símbolo não terminal, que não aparece do lado
direito de nenhuma regra. Isso permite que linguagens sensíveis ao
contexto tenham a sentença vazia.
Dizemos que uma linguagem L é regular (respectivamente sensível ao contexto, livre de contexto) se
existe uma gramática regular (respectivamente sensível ao contexto, livre de contexto) tal que L = L(G),
ou seja, L é a linguagem gerada pela gramática.
Quando passamos para linguagens, passamos a ter inclusões.
O diagrama da direita é chamado hierarquia de Chomsky. Dois fatos interessantes: nem toda gramática
livre de contexto é sensível ao contexto, mas toda linguagem livre de contexto é sensível ao contexto. Isso
acontece pois, dada uma gramática G livre de contexto mas com alguma regra do tipo A → ε , ou seja,
~
que não seja sensível ao contexto, então, como provaremos mais tarde, existe uma gramática G livre de
contexto e sensível ao contexto tal que:
~
L(G) = L( G )
Assim, provaremos que toda linguagem livre de contexto é sensível ao contexto. Isso será feito mais
tarde.
Outro fato interessante: nem todas as linguagens podem ser descritas por gramáticas. Provemos isso.
Dado um alfabeto finito ∑ , o conjunto das possíveis sentenças ∑ * é um conjunto infinito e enumerável.
Uma linguagem L é um subconjunto de ∑ * , logo o conjunto das possíveis linguagens é o conjunto das
partes de ∑ * , ou seja, o conjunto de todos os subconjuntos de ∑ * . Mas esse é um conjunto não
enumerável (Prova diagonal de Cantor).
Como toda gramática pode ser definida por uma cadeia finita de caracteres, o conjunto das possíveis
gramáticas é enumerável. Assim, existem linguagens que não podem ser descritas por gramáticas.
Queremos resolver o problema da análise sintática, isto é, dada uma gramática G e uma sentença
ω determinar se ω ∈ L(G) e, em caso afirmativo, saber qual a derivação.
Vimos que, dado um autômato finito podemos construir uma gramática G regular tal que L(G) é a
linguagem reconhecida pelo autômato. Será que vale a recíproca? Ou seja, dada uma gramática G, existe
um autômato finito cuja linguagem reconhecida por ele é L(G)?
Considere a linguagem L(G), formada pelas seqüências em {0,1}* que terminam em 0 ou 01. Temos que
a gramática pode ser dada por:
S → 0S
S → 1S
S →0
S → 0A
A →1
Tentando aplicar o algoritmo para transformar autômatos em linguagens, chegamos a:
Isso não é um autômato finito, pois a função de transição δ não está bem determinada. Assim, o que
temos é um autômato finito não determinístico.
Outro exemplo de autômato finito não determinístico é o seguinte, que reconhece todas as cadeias com
um número ímpar de 0’s ou um número ímpar e1’s.
Para que um autômato finito não determinístico reconheça uma sentença, deve existir um caminho que
leve ao estado final partindo do estado inicial. Vejamos nos exemplos:
i. Seqüências que terminam em 01.
ii. Seqüências cujo antepenúltimo caractere é 0.
iii. Seqüências que terminam em 01 ou têm um número par de 0’s.
Outra maneira é tomar
Assim, se temos dois autômatos finitos não determinísticos, podemos criar um autômato que reconheça a
linguagem de um ou de outro, que pode ser gerado por:
Onde o novo estado inicial q0 se liga aos estados dos dois autômatos da mesma forma que os estados
iniciais dos autômatos 1 e 2 se ligavam
Assim, se temos autômatos M 1 e M 2 com m1 e m2 estados, respectivamente, um autômato finito não
determinístico que reconhece M 1 ou M 2 tem no máximo m1 + m2 + 1 estados, enquanto um autômato
finito determinístico deveria ter no máximo m1 • m2 estados. Assim, a cota superior é menor para
autômatos finitos não determinísticos.
A definição formal de autômato finito não determinístico é uma 5 – upla M = {Q, ∑, δ , q0 , F } onde:
• Q é o conjunto finito de estados
• ∑ é o alfabeto de entrada
• δ : Q × ∑ → P(Q) : Função de transição
• q0 ∈ Q é o estado inicial
F ⊆ Q é o conjunto de estados finais.
•
Note que o AFND (Autômato Finito Não Determinístico) é bastante semelhante ao determinístico, porém
um estado q e um símbolo a ∈ ∑ levam, não a um estado, mas a um conjunto de estados. Dessa forma,
definimos a linguagem reconhecida por um AFND M = {Q, ∑, δ , q0 , F } como:
^
T ( M ) = {ω ∈ ∑ */ δ (q0 , ω ) ∩ F ≠ 0}
^
Onde δ : Q × ∑ → P(Q) é definido recursivamente como:
^
δ (q, ε ) = {q}
^
^
δ (q, aω ) = ∪ δ (r , ω )
r∈δ ( q , a )
Por exemplo, para o AFND ao lado calculamos:`
^
^
^
δ (q0 , 01) = δ (q0 ,1) ∪ δ (q1 ,1)
^
^
= δ (q0 , ε ) ∪ δ (q2 , ε )
= {q0 } ∪ {q2 } = {q0 , q2 }
Download

LINGUAGENS FORMAIS E AUTÔMATOS