O Pumping Lemma

Os limites das linguagens regulares.

Introdução

Nas secções anteriores avançou-se na resolução do Problema Principal de ALPDada uma linguagem e uma palavra no mesmo alfabeto, determinar se com linguagens regulares.

As expressões regulares e os autómatos finitos (deterministas e não deterministas) são computáveis e eficientes mas ainda falta saber se são adequadas isto é, suficientemente expressivas para definir qualquer linguagem de programação.


As expressões algébricas, de uma forma ou outra, estão presentes em quase todas as linguagens de programação. Uma expressão algébrica é uma palavra como, por exemplo, 2 * (3 + 4) em que certas sub-palavras representam números (2, 3, 4), outras representam operações (+, *) e outras definem a estrutura da expressão ((, )).

A estrutura de uma expressão pode ser visualizada por uma árvore:

Árvore de uma Expressão Algébrica
Figura Árvore Expr Alg
Forma Alternativa
Figura Árvore Expr Alg Alt

Naturalmente, pretende-se que no lugar de 2, 3 e 4 possam estar outras expressões algébricas e que as operações incluam, pelo menos, - e /. Conforme as expressões ficam mais complexas, maior a necessidade e a importância dos parêntesis.

A estrutura de um programa é semelhante à estrutura de uma expressão algébrica: Organiza-se como uma árvore com certos vértices "recursivos". Em vez de números e operações, nas árvores dos programas os vértices têm instruções, expressões, diretivas, etc.

if x > 2:
    a = double(x)
    for i in range(a):
        a = a + i
else:
    a = 0
Árvore de um Fragmento de Programa
Figura Árvore Fragmento Programa

Uma propriedade importante das expressões algébricas é que "os parêntesis têm de estar equilibrados". Descartando os números e as operações, restam apenas os parêntesis e obtêm-se palavras como (), ()(), (())()(()()), que são "válidas" enquanto que as palavras )(, ()), etc devem ser rejeitadas.

A "linguagem dos parêntesis equilibrados" é um excelente teste às linguagens regulares:

  • Se for regular, as linguagens regulares são adequadas para definir as linguagens de programação. De facto, o LISP (ver a página na wikipédia) é uma linguagem de programação que usa parêntesis e pouco mais.
  • Se não for regular, as linguagens regulares não permitem representar árvores de estruturas sintáticas como as expressões algébricas ou os programas python. Nesse caso será necessário tentar outra abordagem para as linguagens de programação.

O problema que se coloca agora é o seguinte: Como saber se uma dada linguagem é, ou não, regular?

Consideremos alguns exemplos (sobre o alfabeto ):

  • é regular.
  • também é regular porque é o complementar de uma linguagem regular (na secção anterior viu-se como fazer a negação de um AFD).
  • também é regular, assim como são , etc.

Neste casos o "exercício" é simples porque ou se encontra uma ER adequada ou são aplicados os resultados teóricos das secções anteriores para construir linguagens regulares.

Quanto a uma versão simples dos parêntesis equilibrados:

As palavras de são Esta linguagem é regular?

De facto, como se prova que uma linguagem não é regular? O problema é o seguinte: Supondo que é uma linguagem regular. Como se prova que é regular? Basta encontrar uma ER (ou AFD ou AFND), , adequada: .

Mas, se não for regular nenhuma ER é adequada. É preciso outro método para provar que não é regular.

Entra o Pumping Lemma.

O Pumping Lemma

Uma observação muito simples:

Um AFD com estados, quando processa uma palavra com mais do que símbolos, tem de "passar" mais do que uma vez em alguns estados, porque há mais símbolos do que estados.

Por exemplo, um AFD para , ilustrado abaixo, tem exatamente dois estados.

AFD para
Figura Exemplo AFD Equivalente 01

Contando as entradas nos estados deste autómato:

PalavraComprimentoEntradas em Entradas em
000
110
............
330
321
312
312
303
303
303
303
............

Todas as palavras de comprimento entram mais do que uma vez em algum estado do autómato. Claramente, o mesmo acontece para palavras de comprimento


Em geral, seja uma palavra que entra duas (ou mais) vezes no estado . Então pode escrever-se em que:

  • é o prefixo de que entra pela primeira vez em .
  • é uma sub-palavra de que parte de e entra de novo em .
  • é o restante sufixo de .

Como (o caminho de) começa e termina em , a sub-palavra pode ser indefinidamente repetida, inclusivamente eliminada, que o estado final da computação não se altera. Isto é, o estado final para é o mesmo que para .

Se for aceite, também são . E, se for rejeitada, também são

Formalmente:

Pumping Lemma. Seja uma linguagem regular e o número de estados de um AFD que a aceita.

Qualquer palavra com pode ser escrita como em que:

  1. .
  2. .
  3. Para qualquer também .

O Pumping Lemma é uma propriedade de todas as linguagens regulares. Dito de outra forma,

Se as conclusões do Pumping Lemma levarem a uma contradição é porque as hipóteses do Pumping Lemma não se verificam. Especificamente: a linguagem considerada não pode ser regular.


Revisitando a linguagem simplificada dos parêntesis equilibrados, .

  1. Supondo que é regular, também é aceite por um certo AFD.
  2. Seja o número de estados desse AFD.
  3. A palavra .
  4. Pelo Pumping Lemma, como então em que:
    1. . Portanto, só tem .
    2. . Portanto tem pelo menos um e nenhum .
    3. Para cada também .

Em , quando é repetido,o número de é alterado. Mas o número de continua a ser . Portanto, quando , o número de deixa de ser igual ao número de .

Isto é uma contradição. Por um lado, se então tem o mesmo número de e de mas por outro, ao repetir , fica com um número diferente de e de .

Portanto a suposição inicial, de que a linguagem é regular, é falsa. A única conclusão possível é que não é uma linguagem regular.

O facto de não ser uma linguagem regular tem consequências profundas:

  • Primeiro, encontrou-se uma linguagem que não é regular.
  • Segundo, as linguagens regulares não são adequadas para definir linguagens de programação.

A incapacidade das linguagens regulares para definir a linguagem simplificada dos parêntesis equilibrados implica a incapacidade para definir as estruturas recursivas (como árvores) necessárias nas linguagens de programação.

Conclusão

O Pumping Lemma aplicado à linguagem mostra que as linguagens regulares não são adequadas para definir linguagens de programação.

Portanto, é necessária outra abordagem para resolver o Problema Principal de ALPDada uma linguagem e uma palavra no mesmo alfabeto, determinar se de forma computável, acessível e adequada.

Nem tudo está perdido. De facto, os conceitos introduzidos até aqui, as ER, os AFD e os AFND, vão continuar presentes no resto do curso e nas suas aplicações. O que muda é a importância que têm: Perdem o título de "candidato preferido" e passam a "contribuição necessária".

O próximo capítulo define certas gramáticas formais que, como vai ser mostrado, generalizam as linguagens regulares e estão associadas aos autómatos de pilha, uma forma simples de autómato com memória ilimitada.