Prévia do material em texto
LINGUAGENS
FORMAIS E
AUTÔMATOS
OBJETIVOS DE APRENDIZAGEM
> Identificar as propriedades do conjunto das linguagens regulares.
> Definir as operações sobre linguagens regulares.
> Aplicar o lema do bombeamento para linguagens regulares.
Introdução
As linguagens regulares são muito importantes para a computação. Elas reme-
tem aos tópicos mais antigos relacionados à teoria da computação e podem ser
aplicadas a uma série de problemas computacionais. Apesar de sua simplicidade,
elas costumam ser usadas em análise léxicas das linguagens de programação.
Ainda, serviram como base para McCulloch e Pitts (1943) na construção de um dos
algoritmos mais importantes da inteligência artificial: o Perceptron.
Neste capítulo, você vai estudar as linguagens regulares e como funcionam os
autômatos finitos determinísticos (AFD) e os autômatos finitos não determinísticos
(AFN) em relação a elas. Ainda, serão apresentadas algumas operações de fecho.
Por fim, você vai ver como funciona o lema do bombeamento, um teorema feito
com o intuito de provar que uma linguagem não é regular.
Propriedades do conjunto das linguagens
regulares
Uma linguagem L é denominada regular se for aceita por algum autômato
finito (AF), de forma que este aceite apenas palavras que fazem parte dela.
Essa é uma linguagem de tipo 3 na hierarquia das linguagens definida por
Chomsky (1956). A hierarquia, conforme Ramos (2008, p. 61):
Linguagens regulares
Carlos Estevão Bastos Sousa
[...] tem como principal mérito agrupar as linguagens em classes, de tal forma que
elas possam ser hierarquizadas de acordo com a sua complexidade relativa. Como
resultado, é possível antecipar as propriedades fundamentais exibidas por uma
determinada linguagem, ou mesmo vislumbrar os modelos de implementação mais
adequados à sua realização, conforme a classe a que pertença.
A Figura 1 apresenta essa hierarquia. Perceba que as linguagens são divi-
didas hierarquicamente em quatro classes (3, 2, 1 e 0), da mais simples para
a mais complexa. Quanto menor o número referente à classe, mais poderosa
ela é. Portanto, a classe 3 é a mais simples, ou seja, a mais restrita.
Figura 1. Hierarquia das linguagens proposta
por Chomsky (1956).
Ao relacionarmos a hierarquia com seus reconhecedores, temos:
� tipo 0, a máquina de Turing;
� tipo 1, a máquina de Turing com fita limitada;
� tipo 2, o autômato de pilha e as gramáticas livres de contexto;
� tipo 3, os autômatos finitos, as expressões regulares e as gramáticas
regulares.
Como visto, as linguagens regulares estão no nível mais baixo da hierar-
quia (tipo 3), ou seja, são as mais simples em termos de complexidade e/ou
eficiência. Isso significa que há uma dificuldade maior em resolver alguns
problemas com esse tipo de linguagem. Em contrapartida, apesar de simples,
elas são muito úteis para as linguagens de programação, pois podem ser
utilizadas em suas análises léxicas.
Linguagens regulares2
Em resumo, compiladores buscam processar determinados comandos
de uma linguagem de programação e, posteriormente, traduzi-los
para uma outra linguagem, a fim de que sejam executados pelo computador.
Keith D. Cooper e Linda Torczon, no livro Construindo compiladores, apon-
tam que a análise léxica é um dos processos que um compilador utiliza para
entender uma determinada entrada, ou seja, o analisador léxico faz a leitura
de um conjunto de caracteres e produz um fluxo de palavras. Assim, agrega
caracteres para formar cadeias, aplicando-as a um conjunto de regras, com o
intuito de determinar se elas são ou não palavras válidas a uma determinada
linguagem de programação.
Conforme Menezes (2011), uma das principais características das lingua-
gens regulares é a representação por formalismo de pouca complexidade,
grande eficiência e fácil implementação. Entretanto, por ser uma linguagem
de tipo 3, possui algumas limitações, como o duplo balanceamento. Pense,
por exemplo, em um texto em que haja o uso de parênteses balanceados,
e a abertura e o fechamento deles sejam encadeados de forma que, para cada
parêntese aberto, exista um fechado. Nesse caso, a solução não é obtida a
partir de uma linguagem regular.
A seguir você compreenderá como os AFs funcionam nessas linguagens.
Uso de autômatos finitos nas linguagens regulares
Os AFs possibilitam a formalização das linguagens regulares. Eles apresentam
inexistência de memória auxiliar, utilizam as transições entre estados dos
caracteres, não têm operação de escrita e dispõem de quantidade de estados
limitada. Basicamente, podemos dividir os AFs em dois tipos: autômatos
finitos determinísticos (AFD) e autômatos finitos não determinísticos (AFN).
Veja, na Figura 2, um exemplo de AFD que, a partir de Σ = {0, 1}, reconhece
palavras que têm a subcadeia 10.
Figura 2. Exemplo de AFD.
Linguagens regulares 3
Perceba que a leitura é iniciada no estado q0. Ao ler o símbolo 0, ele deverá
se manter no mesmo estado. Contudo, ao ler o símbolo 1, haverá uma tran-
sição para o estado q1, que poderá ler o símbolo 1, mantendo-se no mesmo
estado, ou ler o símbolo 0, transferindo-se para o estado q2. Este, por sua
vez, é um estado de aceitação. Assim, caso ocorra a leitura do último símbolo
da cadeia, o processo é finalizado. Em caso contrário, podem ocorrer mais
leituras de símbolos, a partir de uma transição para o próprio estado q2, até
que seja efetuada a leitura do último símbolo da cadeia.
Conforme Sipser (2011), um autômato finito é uma 5-upla (Q, Σ, δ, q0, F), onde:
� Q representa o conjunto finito de estados do autômato (nesse caso,
de acordo com a Figura 2, temos Q = {q0, q1, q2});
� Σ é o alfabeto de entrada, ou seja, Σ = {0, 1} (ele deve ser finito e não
vazio);
� δ representa a função de transição Q × Σ → Q, ou seja, a leitura deve
partir de um determinado estado, ler um símbolo do alfabeto e transitar
para outro ou manter-se no estado atual;
� q0 é o estado inicial e representa o início da leitura;
� F é o conjunto de estados finais, ou estados de aceitação.
Depois de saber como é dividido um autômato, podemos elaborar a notação
tabular do autômato ilustrado (Quadro 1). O símbolo é um apontamento ao
estado inicial, e o negrito representa o estado de aceitação.
Quadro 1. Exemplo de notação tabular de um AFD
0 1
→ q0 q0 q1
q1 q2 q1
q2 q2 q2
De forma análoga, podemos representar o AFN como M = (Q, Σ, δ, q0, F).
Este, por sua vez, tem função de transição (δ) diferente. Isso significa que
o AFN pode fazer transições para vários estados ao mesmo tempo, sendo
capaz, inclusive, de mudar de um estado para outro sem fazer a leitura de
um símbolo (para isso, pode-se utilizar o símbolo ε na transição).
Linguagens regulares4
Veja, na Figura 3, um exemplo de AFN que possibilita a leitura de símbolos
a partir de Σ = {0, 1}, cujas cadeias têm apenas um 0 ao final de cada uma,
sendo os demais símbolos iguais a 1.
Figura 3. Exemplo de um AFN.
Veja, no Quadro 2, a notação tabular do AFN apresentado.
Quadro 2. Exemplo de notação tabular de um AFN
0 1
→ q0 ‒ q0, q1
q1 q2 ‒
q2 ‒ ‒
Note que o não determinismo é apresentado no estado q0, que, ao ler
o símbolo 1, terá a opção entre dois caminhos: continuar no q0 ou transitar
para o estado q1.
Propriedades de decisão para linguagens regulares
As propriedades de decisão possibilitam responder a algumas questões
fundamentais sobre as linguagens regulares. São elas:
� uma linguagem é vazia?
� uma determinada cadeia w pertente à linguagem L?
� há equivalência entre duas descrições de uma mesma linguagem?
Responder se uma dada linguagem é vazia parece um tanto óbvio, pois
Ø = vazio. No entanto, é necessário analisar como a linguagem é representada.
Se ela for representada por um determinado AF e se existir um caminho
do estado inicial até um estado de aceitação, a linguagem não será vazia,
Linguagens regulares 5
mesmo que sua leitura seja ε. Nesse caso, a linguagem será igual a {ε}. Em
caso contrário, a linguagem será vazia.
Verificar apertinência de uma cadeia a uma linguagem é muito simples.
Para isso, basta obter o autômato que represente a linguagem e simular
a leitura da cadeia a ser verificada. Se a leitura da cadeia encerrar em um
estado de aceitação, ela fará parte da linguagem. Caso contrário, não fará.
Na verificação de pertinência de uma cadeia w a uma dada linguagem
L, se a representação de L não for um AF, é possível convertê-la
para um.
Em relação à verificação da equivalência entre linguagens a partir de AFs,
considere os dois AFDs da Figura 4. Ambos aceitam ɛ e cadeias terminadas em 0.
Figura 4. AFDs equivalentes.
Fonte: Adaptado de Hopcroft, Ullman e Motwani (2002).
Podemos tomar ambos os autômatos como um só, considerando-os como
um autômato de cinco estados Q = {q0, q1, q2, q3, q4}, e escolhendo apenas um
dos estados iniciais. Após essa consideração, é possível elaborar uma tabela
com base nos pares de estados.
Inicialmente, inserimos o valor X em todos os pares de estados que pos-
suem apenas um estado de aceitação. Depois, iniciamos a etapa indutiva, na
qual verificamos se os demais estados são distinguíveis ou equivalentes. Eles
são distinguíveis se existe, pelo menos, uma cadeia que seja lida e, ao final,
em um dos casos, pare em um estado de aceitação e, no outro caso, não.
Linguagens regulares6
Ao tabular o preenchimento, o resultado será o Quadro 3.
Quadro 3. Exemplo de notação tabular de um AFD
q1 X
q2 X
q3 X
q4 X X X
q0 q1 q2 q3
Note que {q0, q2}, {q0, q3}, {q1, q4} e {q2, q3} são pares equivalentes. É possível
concluir que eles aceitam a mesma linguagem, considerando que, nesse
exemplo, q0 e q2 são equivalentes e estados iniciais nos dois autômatos.
Operações de linguagens regulares
Conforme Sipser (2011), em aritmética, os objetos básicos são números, e as
ferramentas são operações para manipulá-los, como + (adição) e × (multipli-
cação). Na teoria da computação, os objetos são linguagens, e as ferramentas
incluem operações especificamente desenhadas para manipulá-las. Dentro
desse contexto, as linguagens regulares são fechadas em algumas operações.
Propriedade de fechamento das linguagens regulares
As propriedades regulares são úteis para a construção de novas linguagens
regulares, a partir de linguagens regulares mais simples, tendo a certeza de
que elas também serão linguagens regulares.
De acordo com Hopcroft, Ullman e Motwani (2002), a propriedade de
fechamento, ou fecho, nos permite, a partir de determinadas operações,
elaborar reconhecedores para linguagens que são construídas a partir de
outras linguagens. Essa propriedade é muito útil, pois é por meio dela que
podemos usar operações sobre linguagens regulares cujo resultado também
será uma linguagem regular.
Para Ramos (2008), uma determinada classe de linguagens é fechada em
relação a uma operação se resultar sempre em uma linguagem que também
pertença à classe em questão. Segundo Vieira (2006), a classe das lingua-
gens regulares é fechada nas operações de união, interseção, concatenação,
Linguagens regulares 7
fecho de Kleene e complemento. No entanto, também podemos considerar a
operação de diferença entre essas operações. Veja a seguir.
União
Também representada por L ∪ M = {x | x ∈ L ou x ∈ M}, ao utilizar a operação
de união em duas linguagens (L, M), o resultado será uma nova linguagem,
que conterá todos os símbolos de L, M ou ambas. Conforme Sipser (2011), a
união (Figura 5) simplesmente toma todas as cadeias em ambas as linguagens
e as une em uma linguagem.
Figura 5. Operação de união.
Nesses termos, considere um alfabeto ∑ = {0, 1}. Sejam as linguagens
L = {0, 00, 1} e M = {11, 101}, temos L ∪ M = {0, 00, 1, 11, 101}.
Interseção
A interseção (Figura 6) é representada por L ∩ M = {x | x ∈ L e x ∈ M} e, ao ser
aplicada em duas linguagens, terá como resultado todos os símbolos que
fazem parte de L e M ao mesmo tempo.
Figura 6. Operação de interseção.
Linguagens regulares8
Considere o alfabeto ∑ = {0, 1} e as linguagens L = {0, 00, 1, 10} e M = {0, 1,
10, 11}. Ao aplicar a interseção, o resultado será L ∩ M = {0, 1, 10}, ou seja, uma
nova linguagem que possui os elementos em comum entre L e M.
Complemento
O complemento (Figura 7) é uma operação unária que consiste na geração de
uma nova linguagem, formada pelos símbolos contidos em Σ* (com todas as
combinações possíveis) e que não estão em L, ou seja L– = Σ* – L.
Figura 7. Operação de complemento sobre a linguagem L.
Considerando ∑ = {0, 1} e L = {0, 10, 110}, o complemento é uma nova lin-
guagem com todas as cadeias, exceto as existentes em L, ou seja, L– = {ε, 1,
00, 01, 010, 001, …}.
Diferença
A operação diferença (Figura 8), também representada por L – M = L ∩ M– = {x | x
∈ L e x ∉ M}, é considerada a interseção entre a linguagem L e o complemento
da linguagem M. Assim, quando ela é aplicada em duas linguagens, resulta
nos símbolos existentes apenas na primeira.
Figura 8. Operação de diferença entre duas
linguagens.
Linguagens regulares 9
Perceba que ∑ = {0, 1}, dadas as linguagens L = {0, 1, 01, 10} e M = {0, 01,
111}. Ao aplicar a operação de diferença, teremos o resultado L – M = {1, 10}.
Logo, a nova linguagem terá os símbolos contidos em L, mas que não os que
fazem parte de M.
Concatenação
A concatenação, representada por L . M = {xy | x ∈ L e y ∈ M}, é formada pelo
conjunto de símbolos concatenados entre L e M. Assim, considere ∑ = {a, b, c,
…, z}, L = {a, b} e M = {c, d, e}. Logo, L . M = {ac, ad, ae, bc, bd, be}. Nesse sentido,
é possível afirmar que a operação de concatenação é a combinação entre os
elementos das linguagens utilizadas.
Fechamento de Kleene
O fechamento de Kleene (1951) é aplicado apenas a uma linguagem, ou seja,
é uma operação unária. Ele pode ser usado de duas maneiras: usando o
operador * ou +. Como resultado dessa aplicação, é possível formar diversas
cadeias a partir de qualquer número de símbolos de uma dada linguagem L.
Com o uso de *, a operação é chamada de fecho estrela. Com +, é denominada
soma de Kleene.
A diferença entre os dois operadores é relacionada ao uso do símbolo
ε, que representa uma cadeia vazia. Assim, considerando ∑ = {a, b, c, …, z} e
L = {a, b}, temos L* = {ε, a, b,aa, ab, bb, ba, ...} e L+ = {a, b, aa, ab, bb, ba, ...}.
Perceba que L+ não considera ε como parte integrante da linguagem.
Note também que os dois casos consistem em uma generalização da con-
catenação, ou seja, geram uma nova linguagem formada a partir de qualquer
combinação entre os símbolos existentes na primeira. No entanto, como citado,
ao usar o operador estrela, as cadeias vazias também são consideradas.
É importante salientar que, segundo Hopcroft, Ullman e Motwani (2002),
o fechamento de Kleene de uma linguagem L representa o conjunto dos
símbolos que podem ser formados ao se tomar qualquer número de símbolos
de L, concatenando-os e tendo possíveis repetições de símbolos. Veja um
exemplo a seguir.
Linguagens regulares10
Seja o alfabeto ∑ = {a, b, c, …, z}, considere L = {a, b}. Logo:
L0 = {ε},
L1 = {a, b},
L2 = {aa, bb, ab, ba},
...,
L* = {ε, a, b, aa, ab, bb, ba, ...}.
Veja que, de acordo com Hopcroft, Ullman e Motwani (2002), L* é a união infinita
∪i ≥ 0 Li, na qual L0 = {ε}, L1 = L e L1 para i > 1 é LL… L, ou seja, uma concatenação
sucessiva de L a partir de i cópias.
Perceba que o símbolo apresentado junto à linguagem se refere ao nível
de concatenação entre os símbolos contidos nela. À medida que esse número
aumenta, também aumenta o tamanho das combinações elaboradas. Note,
no exemplo apresentado, que a linguagem L* é uma linguagem infinita.
Lema do bombeamento para linguagens
regulares
Segundo Menezes (2011), para mostrar que uma linguagem é regular, basta
representá-la a partir de um AF. Entretanto, provar que uma linguagem é não
regular é um pouco mais complexo. Assim, faz-se necessário desenvolver
um processo para cada caso, ou seja, analisar a linguagem a ser verificada.
Uma ferramenta bastante útil é o lemado bombeamento. Esse teorema é
fundamentado no princípio da casa dos pombos, que tem como base a ideia
de que, se em um pombal há mais pombos do que casas, deve existir pelo
menos uma casa com pelo menos dois pombos.
Ainda sobre o lema do bombeamento:
Este lema especifica uma propriedade que qualquer linguagem regular tem. Além
destas, outras linguagens (não reconhecidas por AFDs) também têm esta mesma
propriedade. Assim, se mostrar que uma linguagem L satisfaz o lema do bombea-
mento, isto não implica que L é regular. Mas se mostrar que L não satisfaz o lema do
bombeamento, pode-se concluir que L não é linguagem regular (VIEIRA, 2006, p. 97).
Linguagens regulares 11
O comprimento de uma palavra está relacionado à quantidade de
caracteres/letras que ela tem. Assim, considere a palavra w = abcde.
Podemos reescrever o comprimento como |w| ou |abcde|. Nesse caso, ela possui
valor igual a 5.
Salienta-se que |ε| = 0. Veja mais dois exemplos:
w = engenharia sob o alfabeto {a, b, c, d, e, f, ..., z}
|w| = 10
w = 110011 sob o alfabeto {0,1}
|w| = 6.
Vamos compreender como funciona esse lema, conforme Sipser (2011).
Considere L uma linguagem regular. Existe um número p, também deno-
minado comprimento do bombeamento, tal que toda cadeia w ∈ L, ou seja,
qualquer palavra pertença a L. O comprimento dessas cadeias w é maior ou
igual a p, portanto, |w| ≥ p. Assim, w pode ser escrita da forma w = xyz, isto
é, w pode ser dividida em três subcadeias, em que:
� y não pode ser vazio (y ≠ ε);
� o comprimento das subcadeias xy devem ser menores ou iguais a p,
ou seja, |xy| ≤ p;
� todo y deve ser bombeado 0 ou mais vezes, e a palavra resultante deve
pertencer à linguagem L, isto é, ∀ ≥ 0, ∈ L.
Ao consideramos w = 101, onde w é uma palavra que poderá ser dividida
como = 1 0 1 , y deve ser bombeado. Assim, conforme w = xyiz, obte-
remos outras palavras, de acordo com o valor contido em i. Palavras como
11, 101, 1001, 10001, entre outras, poderão ser obtidas a partir de uma dada
linguagem L.
Como visto neste capítulo, uma linguagem é regular se existir um AF que
a represente. Veja o exemplo de AFD da Figura 9.
Linguagens regulares12
Figura 9. Exemplo de AFD com looping.
Considerando o autômato apresentado, percebemos que:
� y ≠ ε, ou seja, possui um valor mesmo que não seja bombeado;
� ao utilizarmos os estados q0 e q1, podemos perceber que eles têm
tamanho menor ou igual ao tamanho total do autômato (|xy| ≤ p);
� para cada execução, a saída será uma palavra que pertencerá à lingua-
gem definida. Assim, y poderá ser bombeado várias vezes. Dessa forma,
é possível representar as subcadeias como xyiz, onde i representa a
quantidade de vezes em que o bombeamento é executado. Com isso,
podemos produzir palavras como xz, xyz, xyyz, entre outras.
O lema do bombeamento é utilizado apenas para linguagens infinitas.
O nome bombeamento refere-se a bombear um looping.
Anteriormente, montamos um AF e, por isso, sabíamos que o resultado
seria uma linguagem regular. Contudo, o que fazer quando não sabemos se
há a possibilidade de criar um AF?
A resposta é dada por Hopcroft, Ullman e Motwani (2002). Eles afirmam
que, com frequência, é mais fácil provar que uma afirmação não é um teorema
do que provar que ela é um teorema. Assim, normalmente, usamos o lema
do bombeamento e provamos que uma linguagem é não regular a partir de
uma prova por contradição. Veja os passos a seguir.
1. Assumimos que L é uma linguagem regular.
2. Escolhemos uma palavra cujo tamanho seja maior ou igual a p.
Linguagens regulares 13
3. Iniciamos o processo de bombeamento, decompondo w em xyz e ge-
rando uma nova palavra com formato xyiz. Dessa forma, se para um
determinado valor i for encontrada uma palavra que não pertença a
L, a linguagem analisada não é regular. Note que apenas uma palavra
encontrada já poderá provar que a linguagem verificada não é regular.
Considerando ∑ = {a, b}, prove que existe uma linguagem L = {a, b}*
que possui a mesma quantidade de a e b (ou até mesmo nenhuma),
ou seja, que {L = ∈ { , }∗ | = ≥ 0} não é regular.
Para isso, vamos dividir a resolução em três passos.
1) Assuma L como uma linguagem regular.
2) Considere |w| ≥ p. Logo, podemos considerar a palavra w = aaaabbbb.
3) Tomando ainda a palavra = aa
, ao bombearmos para i = 2,
teremos = aa
. Isso significa que não existe uma mesma
quantidade de a e b e, portanto, W não pertence à linguagem L.
Assim, {w ∈ {a, b}* | w = anbn e n ≥ 0} não produz uma linguagem regular.
Referências
CHOMSKY, N. Three models for the description of language. Cambridge: Massachusetts
Institute of Technology, 1956. Disponível em: https://chomsky.info/wp-content/uplo-
ads/195609-.pdf. Acesso em: 28 jan. 2021.
HOPCROFT, J. E.; ULLMAN, J. D.; MOTWANI, R. Introdução à teoria de autômatos, linguagens
e computação. 2. ed. São Paulo: Campus, 2002.
KLEENE, S. C. Representation of events in nerve nets and finite automata. Santa Monica:
The Rand Corporation, 1951. Disponível em: https://www.rand.org/content/dam/rand/
pubs/research_memoranda/2008/RM704.pdf. Acesso em: 28 jan. 2021.
MCCULLOCH, W. S.; PITTS, W. A logical calculus of the ideas immanent in nervous activity.
The Bulletin of Mathematical Biophysics, v. 5, n. 4, p.115-133,1943.
MENEZES, P. B. Linguagens formais e autômatos. 6. ed. Porto Alegre: Bookman, 2011.
RAMOS, M. V. M. Linguagens formais e autômatos. Juazeiro: UNIVASF, 2008. Disponí-
vel em: http://www2.fct.unesp.br/docentes/dmec/olivete/lfa/arquivos/Apostila.pdf.
Acesso em: 29 jan. 2021.
SIPSER, M. Introdução a teoria da computação. 2. ed. São Paulo: Cengage Learning, 2011.
VIEIRA, N. J. Introdução aos fundamentos da computação: linguagens e máquinas. São
Paulo: Thomson, 2006.
Linguagens regulares14
Leituras recomendadas
COOPER, K. D.; TORCZON, L. Construindo compiladores. 2. ed. Rio de Janeiro: Campus, 2014.
JUDITH, L. G. Fundamentos matemáticos para a ciência da computação. 3. ed. Rio de
Janeiro: LTC, 1995.
LEWIS, H. R.; PAPADIMITRIOU, C. H. Elementos de teoria da computação. 2. ed. Porto
Alegre: Bookman, 2000.
Os links para sites da web fornecidos neste capítulo foram todos
testados, e seu funcionamento foi comprovado no momento da
publicação do material. No entanto, a rede é extremamente dinâmica; suas
páginas estão constantemente mudando de local e conteúdo. Assim, os editores
declaram não ter qualquer responsabilidade sobre qualidade, precisão ou
integralidade das informações referidas em tais links.
Linguagens regulares 15