Ir para o conteúdo

CCT-UFCA/Ciência da Computação/Autômatos e Linguagens Formais/Corretude de Expressões Regulares

De Wikiversidade

Similarmente à corretude dos Autômatos Finitos, veremos formas de provar a corretude de Expressões Regulares para observarmos o funcionamento deles mais atentamente.

Definições

[editar | editar código]
  • Sejam A e B dois conjuntos. É verdade que, se xA e AB, então xB;
  • Sejam R e S são expressões regulares. Temos que é verdade que (R)* = (R)*(R)*; que L(S) ⊆ L(R) =⇒ L((S)*) ⊆ L((R)*); e ainda que L((R)*) ⊆ L((SR)*).

Corretude de ERs

[editar | editar código]

Seja R uma expressão regular e L uma linguagem. Dizemos que R é correta em relação à L, se L = L(R). Quando L está clara pelo contexto, podemos dizer apenas que R é correta.

Compreender como provar a corretude de uma expressão regular é bastante importante dada as suas várias aplicações. Alguns exemplos de aplicação para expressões regulares são:

  1. Validação de Formatos: Verificação de formatos de e-mail, números de telefone, endereços IP, códigos postais, senhas etc.;
  2. Pesquisa e Substituição: Encontrar e substituir trechos específicos de texto em documentos ou na localização de padrões complexos em arquivos de log ou dados textuais;
  3. Extração de Dados: Extração de partes específicas de texto de documentos ou páginas web (web scraping), e coletamento de informações estruturadas a partir de dados não estruturados; e
  4. Linguagem de Programação e Compiladores: Na análise léxica em compiladores define tokens em código-fonte, e highlighting de sintaxe em editores de texto e IDEs.

Para provar que uma expressão regular R é correta em relação a uma linguagem L, é necessário provar que L = L(R). Para isso, podemos, ou calcular a linguagem L(R) a partir da própria expressão R e averiguar se temos diretamente que L(R) = L, ou provar que xL(R) se e somente se xL.

Quando for necessário provar que xL(R) se e somente se xL, a prova da implicação xL(R) ⇒ xL normalmente é feita de forma direta, provando propriedades sobre as subexpressões de R. As propriedades são sempre da forma x ∈ L(R') ⇒ P(x), onde R' é uma subexpressão de R e P é uma predicado que denota uma propriedade sobre a cadeia x. A ideia é provar propriedades sobre as expressões menores e usá-las para provar propriedades sobre expressões maiores até que se tenha o suficiente para provar a implicação xL(R) ⇒ xL, que é a ida em si e é a última propriedade a ser provada.

Finalmente, a prova da implicação xLxL(R) normalmente é feita por indução finita no tamanho de x, mas pode ser feita também em outro aspecto numérico de x, como o número de símbolos em x de determinado tipo. Primeiramente, precisa-se provar o caso base, ou seja, x = ε. Depois, no passo indutivo, para um natural k arbitrário maior do que as cadeias no caso base, assume-se que a implicação vale para todas as cadeias de tamanho menor do que k para então provar que a implicação vale para todas as cadeias de tamanho k.

Exemplo 1: Considere a linguagem L = {0n1m| n, m ≥ 1} sob o alfabeto Σ = {0,1}. Uma ER que representa L é a R = 0+1+.Temos que:

Assim, temos diretamente que L(R) = L e, portanto, provar a equivalência xL(R) ⇔ xL não é necessário.

Exemplo 2: Considere a linguagem L = {w | w possui um número par de símbolos 0} sob o alfabeto Σ = {0,1}. Uma ER que representa L é a R = (1 ∪ 01*0)*. Note que L(R) ○ L(R) = L(R).

Provando a Ida ( xL(R) ⇒ xL ): Se xL(R), temos que x = w1w2 ... wk, onde cada wiL(1 ∪ 01*0). Como toda string em L(1) ou L(01*0) possui um número par de símbolos 0 (a string em L(1) com zero símbolos 0 e as strings em L(01*0) sempre com exatamente dois símbolos 0), então cada wi tem um número par de símbolos 0. Como o número de símbolos 0 em x é igual a soma dos símbolos 0 de todas as substrings wi e cada wi tem um número par de símbolos 0, então x tem um número par de símbolos 0.

Provando a Volta ( xLxL(R)): Vamos provar por indução em |x| que, xLxL(R). Se |x| = 0, x = ε. De fato, ε ∈ L e ε ∈ L(R). Logo, a implicação é válida para x = ε.

Seja k um inteiro arbitrário maior do que zero. Suponha que, para toda string x com no máximo k − 1, xL(R). Vamos provar que, para toda string x com k símbolos, xL(R). Se xL, a implicação xLxL(R) é verdadeira por conta da premissa falsa. Então, suponha que xL. Se x = 1k , então claramente xL(R), visto que L(1*) ⊆ L(R).

Seja então x ∈ L uma string de tamanho k que contém pelo menos um símbolo 0. Como xL, então x tem pelo menos dois símbolos 0. Seja i a posição do penúltimo símbolo 0. Definimos a cadeia z como sendo o sufixo de x a partir da posição i e a cadeia y o prefixo até a posição i − 1 de forma que x = yz. Note que y pode ser a cadeia vazia, bastando para isso que x = z. Por essa definição, temos que z possui apenas dois símbolos 0 e inicia com um deles, ou seja, zL(01*01*) ⊆ L(1 ∪ 01*0)* = L(R).

Temos que y tem um número par de símbolos 0, pois x tem um número par de símbolos 0 e z possui dois zeros. Logo, yL. Adicionalmente, podemos afirmar que |y| < |x| visto que z não é a cadeia vazia. Portanto, usando a hipótese indutiva, podemos afirmar que yL(R). Como x = yz e y, zL(R), então x ∈ L(R) ○ L(R) = L(R).