Alfabetos, Palavras e Linguagens

Era uma vez, num reino muito muito distante, ...

Símbolos e Alfabetos

Alfabeto, Símbolo. Um alfabeto é um conjunto finito de símbolos, também designados letras (em inglês: symbols, letters, tokens).

Os alfabetos são normalmente representados por maiúsculas gregas (sigma) e (gama) e os símbolos por minúsculas romanas .

Por exemplo:

  • O alfabeto do português tem os seguintes símbolos: .
  • Os símbolos e formam um alfabeto binário. Outro alfabeto binário muito usado em ALP é .
  • Os números naturais podem ser escritos em base 10 usando o alfabeto .
  • Para as expressões algébricas usa-se o alfabeto .

As linguagens de programação têm um conjunto de palavras reservadas, como while, if, etc. que são atómicas, isto é, não podem ser divididas.

O símbolo "if" não é, em termos da linguagem de programação, um "i" seguido de um "f", mas um símbolo que não pode ser decomposto.

Já fragmentos de programas como if (x == 42) { return "yes"; } else { return "no"; } são compostos. Nesta instrução condicional (x == 42) é o teste (ou guarda), { return "yes"; } o bloco positivo e { return "no"; } o bloco negativo. Neste caso a instrução resulta de uma regra do tipo

Condicional := if ( Condição ) { Instruções } else { Instruções }

Por sua vez a guarda (Condição) e os blocos de Instruções podem ser decompostos, até chegarmos a símbolos atómicos como (, ==, return, etc.

Palavras

Palavra. Uma palavra sobre um alfabeto é uma sequência finita de símbolos desse alfabeto (em inglês: word).

Formalmente, uma palavra de comprimento é uma função que a cada inteiro .

As palavras são normalmente representadas pelas minúsculas romanas .

Palavra Vazia. A palavra vazia (em inglês: empty word, null, nil) é a única palavra de comprimento 0 e representa-se pela letra grega (lambda). Também é comum usar-se ou (epsilon) para representar a palavra vazia.

Não confundir a palavra (que tem comprimento 0) com um símbolo. Estes, como palavras, têm comprimento um.

Propriedades do Comprimento das palavras

  • O comprimento da palavra é normalmente representado por .
  • Para a palavra vazia, . Em geral, se então . Isto é, .

Fecho de Kleene

Dado um alfabeto, interessa considerar todas as palavras que podem ser formadas com essas letras.

Por exemplo, para representar números em binário usam-se palavras sobre . Mas, depois de se definirem todas as palavras, há que determinar quais as que interessam. Por exemplo, embora e sejam palavras binárias (distintas), se interpretadas normalmente, ambas correspondem ao número e portanto há aqui uma ambiguidade. Será possível definir uma linguagem que continue a representar todos os números mas sem ambiguidades?

Para abordar estas questões tem de se começar por definir rigorosamente o conjunto de todas as palavras que se podem obter com um dado alfabeto.

Fecho de Kleene. O fecho do alfabeto é o conjunto de todas as palavras sobre , representa-se por e fica definido pelas seguintes condições:

  • base .
  • passo Se e então .
  • fecho Qualquer palavra só pode ser obtida por um número finito de aplicações do passo a começar na palavra vazia. E reciprocamente, qualquer palavra obtida a partir de por aplicações do passo em número finito está em .

Esta definição exclui a possibilidade de palavras infinitas. Mas isto não significa que exista um limite ao comprimento das palavras em . Pelo contrário, se então, para qualquer existe pelo menos uma palavra de comprimento .

Exercício: Porquê? E porque é que acima é referida a condição ""? O que é ?

No fecho de um alfabeto também ficam excluídas palavras formadas por símbolos de outros alfabetos. Por exemplo, a palavra não está em .

Logo a primeira condição na definição de inclui a palavra vazia. Como muitas vezes é conveniente excluir define-se

Exercício: Mostre que .

Operações com Palavras

Comprimento

Ainda não foi apresentada uma definição rigorosa de comprimento de uma palavra. Usando um esquema recursivo:

Comprimento. A função comprimento é calculada pelas seguintes regras:

  • base .
  • passo , com .

Por exemplo, para calcular :

  • Como , aplicar-se a regra do passo: fazendo tem de ser e , e obtem-se .
  • Ainda não está calculado o valor de . Tornando a aplicar o passo: .
  • Uma nova aplicação do passo: .
  • Finalmente, pela base: , portanto e, sucessivamente, obtém-se e .

Também é comum usar-se a notação para indicar o número de ocorrências do símbolo na palavra .

Por exemplo: .

Concatenação

O operação fundamental das palavras é a "concatenação". A concatenação de hello com world é helloworld. Para definir rigorosamente esta operação:

Concatenação. A concatenação (ou produto) de duas palavras representa-se por ou apenas e é uma operação binária definida por:

  • base Se então .
  • passo Se então
    1. Existe uma palavra e uma letra tal que .
    2. .

Por exemplo, para calcular a concatenação de com :

  • O comprimento de é 2, portanto temos de aplicar o passo: .
  • Precisamos de calcular . Pela regra do passo: .
  • Finalmente, é calculada pela regra base: .
  • Andando para trás: e, portanto, .

Potência

A repetição de concatenações define as potências, tal como a multiplicação dos números.

Potências. As potências da palavra são (informalmente):

Por exemplo, mas , seguindo as regras normais de precedência das operações.

Inversa

Finalmente, as palavras (enquanto sequências de símbolos) podem ser escritas "de trás para a frente":

Inversa. A inversa de , representada por (ou ) é calculada pelas seguintes regras:

  • base Se então .
  • passo Se então e .

Por exemplo, se então .

Propriedades das Operações de Palavras

O que acontece à inversa de duas palavras concatenadas?

Para quaisquer palavras ,

Demonstração (informal): Se a conclusão é evidente. Caso contrário e com todos os . Então:

O fecho também tem propriedades interessantes.

Se forem alfabetos,

  • .
  • .
  • .
  • Se então é infinito.
  • Se então .

Por fim, a concatenação tem propriedades semelhantes às do produto (de números).

Sejam .

  • associativa .
  • elemento neutro .
  • não comutativa Em geral, .
  • aditiva .
  • unicidade Cada palavra só pode ser escrita de uma única forma como concatenação de símbolos. Isto é, se e e então (mesmo comprimento) e para cada (mesmos símbolos) .

Ordenações de Palavras

Uma palavra pode "conter" outra. Por exemplo, palavra contém pala no início, ala no "meio" e lavra no fim. Estes três casos ilustram as definições que se seguem:

Subpalavra, prefixo, sufixo. Sejam .

  • subpalavra se .
  • prefixo se .
  • sufixo se .

Fica claro que os prefixos são casos particulares das subpalavras (quando ) e que cada palavra é subpalavra de si própria (quando ).

Outro caso importante é a ordem lexicográfica (dos dicionários), onde se usa a ordem das letras para ordenar as palavras.

Ordem Lexicográfica. Supondo que o alfabeto está ordenado, . Então se:

  • .
  • Caso contrário, e com .

Esta definição abrange dois casos:

  1. por < porque porque por é um prefixo de porque.
  2. porque < portugal porque, como porque = por_q_ue e portugal = por_t_ugal então portugal e porque têm um prefixo comum, por, e "divergem primeiro" nas letras q e t. Como q < t então porque < portugal.

A ordem lexicográfica tem um problema interessante: Quantas palavras existem entre e , num alfabeto binário?

Por exemplo [Exercício: porquê?]. Mas também . De facto, para qualquer . Portanto, seguindo a ordem lexicográfica, há infinitas palavras entre e .

Esta conclusão, embora dramática, mostra que a ordem lexicográfica é inútil na importante tarefa de enumerar (isto é, listar) todas as palavras por ordem. Para resolver este problema é necessária mais uma ordem entre palavras:

Ordem Mista. Dadas duas palavras , se ou ( e ).

Dito de outra forma, a ordem mista define o seguinte processo:

  1. Primeiro, as palavras são agrupadas por comprimentos iguais: todas as palavras de comprimento 0 antes das de comprimento 1, todas as de comprimento 1 antes das de 2, etc.
  2. Depois, em cada grupo, as palavras (todas com o mesmo comprimento) são ordenadas lexicograficamente.

Revisitando o exemplo anterior:

  • e têm o mesmo comprimento. Portanto, são comparadas lexicograficamente: porque .
  • O comprimento de é maior que o de . Portanto, .
  • e têm o mesmo comprimento. Lexicograficamente, portanto .

De facto a ordem mista é uma ferramenta essencial porque permite "avançar passo-a-passo" no conjunto de todas as palavras, sem deixar nenhuma de fora. Mais concretamente:

A ordem mista define uma sequência de todas as palavras. No caso do alfabeto binário, com :

Sucessor

A ordem mista permite começar na primeira palavra, avançar para a seguinte, depois para a seguinte, etc. da mesma forma são contados os números: 0, 1, 2, etc.

A passagem de um número para o seguinte (x++) tem nome, sucessor, que também se aplica às palavras:

Sucessor. Seja um alfabeto ordenado. A função sucessor tem assinatura e calcula-se pelas seguintes regras:

  • base .
  • passo Se então . Caso contrário, .

Voltando ao exemplo anterior:

Visto de outra forma, usando o sucessor obtém-se uma sequência que começa na palavra vazia e percorre todas as palavras:

Há uma clara relação entre a ordem mista e a função sucessor.

Seja a sequência de todas as palavras de que se obtém com a ordem mista e uma palavra qualquer.

  1. .
  2. Se então existe uma palavra tal que .
  3. .

O que este teorema afirma, especialmente na igualdade , é que a ordem mista e a função sucessor são aspetos diferentes da mesma enumeração das palavras.

Linguagens

Numa linguagem de programação nem todas as palavras possíveis correspondem a programas válidos. Por exemplo:

2 return) *
    : dobro def (x

é uma palavra que pode ser formada com o alfabeto do python, mas não é um programa.

É necessário, de alguma forma, distinguir as palavras que são "válidas" das outras que não o são. De novo, encontramos na matemática exatamente as ferramentas necessárias.

Linguagem. Uma linguagem é um conjunto de palavras sobre um certo alfabeto. Isto é, é uma linguagem sobre se .

Esta definição de linguagem parece dizer pouco, mas por enquanto serve exatamente para o que se pretende.

Por exemplo, se for a linguagem dos programas python então mas .

Mais tarde será enunciado e tratado o Problema Principal de ALP:

Problema Principal de ALP (versão 0). Dada uma linguagem e uma palavra no mesmo alfabeto, determinar se .

Entretanto, como uma linguagem é (apenas) um conjunto de palavras, tem um certo número de elementos, que pode ser infinito (quantos programas python existem?): Se for uma linguagem, representa o número de elementos de .

Outros exemplos de linguagens são:

  • binárias , , etc.
  • As palavras do português, sobre o alfabeto romano. Por exemplo, a, aba, abade, etc. mas não aa, prof, tásse, xkcd, etc.
  • Os programas de uma linguagem de programação.
  • As frases do português, sobre o alfabeto formado pelas palavras do português. Por exemplo bom dia, a aula é longa, etc. mas não dia dia dia, aula a é longa, etc.

Muitas vezes é útil visualizar-se uma linguagem de forma a explorar a estrutura das suas palavras. Isso pode ser feito através de árvores:

Árvore de uma Linguagem. Seja uma linguagem sobre .

Os vértices (ou nós) da árvore de (ou diagrama de ) são palavras de e cada aresta (ou arco) tem um símbolo de . Além disso, todas as arestas são da seguinte forma: se e então é uma aresta. A raíz da árvore é a palavra vazia.

Cada vértice é representado por um círculo com a respetiva palavra lá dentro. Uma palavra da linguagem é representada por um círculo duplo.

Por exemplo, tem a seguinte árvore (onde os vértices irrelevantes foram omitidos):

Árvore (parcial) de
Árvore de uma Linguagem
As palavras de estão em círculos duplos.

Operações com Linguagens

Como conjuntos, as linguagens podem ser combinadas pelas operações comuns: união, intersecção, etc. Além disso, como os seus elementos são palavras, podem ser definidas operações adicionais, específicas das linguagens (concatenação, fecho, etc).

Operações com Linguagens. Sejam linguagens sobre . Estão definidas as seguintes operações de conjuntos:

  • união .
  • intersecção .
  • subtração .
  • complemento .

Adicionalmente, usando as operações de palavras:

  • concatenação .
  • potências .
  • fechos .
  • inversão .

Alguns exemplos das operações específicas das linguagens: Sejam . Então:

  • .
    • As palavras de foram formadas escolhendo primeiro uma palavra de e depois uma palavra de .
    • A palavra pode ser obtida de suas formas: e .
  • .
  • .
  • .

Confusões com a palavra vazia, o conjunto vazio, etc.

  1. A palavra vazia, , não é um símbolo. Qualquer símbolo, como palavra, tem comprimento 1. A palavra vazia tem comprimento 0.
  2. O conjunto vazio, , tem 0 elementos. Para tornar este facto mais explícito também se escreve .
  3. O conjunto tem um elemento, . Portanto, não é o conjunto vazio.

Exemplo. Aqui (e só no âmbito deste exemplo) estamos a distinguir explicitamente os símbolos usando plicas, como em 'a', e as palavras usando aspas, como em "ab".

  1. O alfabeto {a, b} tem dois símbolos: 'a' e 'b'.
  2. Uma palavra é uma (qualquer) sequência finita de símbolos.
    • Por exemplo "abba", "baba", "aa", etc.
  3. Todas as palavras de comprimento 2 são "aa", "ab", "ba", "bb".
  4. Também poderíamos listar todas as palavras de comprimento 3 (num total de oito palavras), etc.
  5. Todas as palavras de comprimento 1 são "a" e "b".
    • Se quiséssemos ser terrivelmente rigorosos, teríamos de dizer que 'a' e "a" são diferentes porque 'a' é um símbolo e "a" é uma sequência de símbolos, tal como, por exemplo, o inteiro 1 é completamente diferente de [1], que é uma lista de inteiros.
    • Raramente é necessário distinguir explicitamente os símbolos das palavras de comprimento um.
  6. Só há uma palavra com comprimento zero. Como sequência, "" é a (única) com zero símbolos. Em vez de "" escreve-se λ (ou ε ou ϵ).
    • λ não é um símbolo mas uma palavra. Escrita por extenso é "".
  7. Usando a concatenação de palavras podemos escrever, por exemplo: "abba" = "ab" · "ba".
    • Outras formas de obter "abba" são "abba" = "a" · "bba", "abba" = "abb" · "a", etc.
    • Em particular também "abba" = "abba" · λ ou "abba" = λ · "abba" ou mesmo "abba" = λ · "a" · λ · "bba".
    • Nestes exemplos o λ nunca aparece entre "..." pois isso é o equivalente a um erro sintático. Também não escrevemos 2 */ 3 quando fazemos contas...
  8. Em geral não se escrevem os símbolos como 'a', as palavras como "abab" nem as concatenações como "aa" · "ba".
    • Portanto, em vez de "abba" = λ · "a" · λ · "bba" o que escrevemos é simplesmente abba = λaλbba.
    • Este abuso de notação pode facilitar a leitura errada de λ como um símbolo, à semelhança de a e b.