Frank Coelho de Alcantara
Considere o seguinte código para o cálculo de $x$ elevado a $y$.
#include <stdio.h>
int potencia(int x, int y) {
int resultado = 1;
for (int i = 0; i < y; i++) {
resultado *= x;
}
return resultado;
}
int main(void) {
printf("2 elevado a 8 : %d\n", potencia(2,8));
return 0;
}
Como você prova que este código faz o cálculo deste produto?
Antes da formalização da lógica, antes de Leibnitz, Boole, Hilbert, Gödel, Church e Turing, uma prova era só, e somente só, um argumento convincente.
Convincentes são os argumentos que não deixam dúvidas no interlocutor. De fato, ainda hoje, uma prova é apenas um argumento convincente. O que mudou é que este convencimento deve ser atingido por meio da lógica.
Poderíamos definir uma prova como: uma sequência de fórmulas, na qual, cada fórmula ou é um axioma, ou é o resultado da inferência da fórmula anterior.
Geralmente, não precisamos de toda esta formalidade matemática para provar que um programa funciona. Contudo, esta formalidade garante que o programa funciona.
int potencia(int x, int y) {
int resultado = 1;
for (int i = 0; i < y; i++) {
resultado *= x;
}
return resultado;
}
A parte mais importante do código é a função $potencia$ e nesta função, a parte principal é o laço. Desta forma, precisamos provar que este laço faz o que desejamos. Começamos com definindo um loop invariant.
Loop Invariant: uma propriedade dos laços que será verdadeira, antes e depois de cada iteração. No caso, podemos começar com: $(y \ge i ) \wedge (resultado = x^i) = True$ e verificar se isto é verdade, ou não, antes das iterações, a cada interação e depois de todas as iterações Hoare Logic.
Estado $(resultado = 1) \wedge (i=0) \wedge (i \lt y)$ | Invariante $(y \ge i ) \wedge (resultado = x^i)$ | |
---|---|---|
$(resultado = 1) \wedge (i=0) \wedge (0 \lt 8)$ | $(8 \ge 0 ) \wedge (1 = 2^0) \equiv True$ | |
0 | $(resultado = 2) \wedge (i=1) \wedge (1 \lt 8)$ | $(8 \ge 1 ) \wedge (2 = 2^1) \equiv True$ |
1 | $(resultado = 4) \wedge (i=2) \wedge (2 \lt 8)$ | $(8 \ge 2 ) \wedge (4 = 2^2) \equiv True$ |
2 | $(resultado = 8) \wedge (i=3) \wedge (3 \lt 8)$ | $(8 \ge 3 ) \wedge (8 = 2^3) \equiv True$ |
3 | $(resultado = 16) \wedge (i=4) \wedge (4 \lt 8)$ | $(8 \ge 4 ) \wedge (16 = 2^4) \equiv True$ |
4 | $(resultado = 32) \wedge (i=5) \wedge (5 \lt 8)$ | $(8 \ge 5 ) \wedge (32 = 2^5) \equiv True$ |
5 | $(resultado = 64) \wedge (i=6) \wedge (6 \lt 8)$ | $(8 \ge 6 ) \wedge (64 = 2^6) \equiv True$ |
6 | $(resultado = 128) \wedge (i=7) \wedge (7 \lt 8)$ | $(8 \ge 7 ) \wedge (128 = 2^7) \equiv True$ |
7 | $(resultado = 256) \wedge (i=8) \wedge (8 \lt 8)$ | $(8 \ge 8 ) \wedge (256 = 2^8) \equiv True$ |
Sabemos que $(y \ge i ) \wedge (resultado = x^i)$ realmente é invariante.
Sabemos que $(i \not< y )$. Vimos que isso não ocorre no laço.
Desta forma podemos afirmar que $resultado = x^y$.
Provado. Ainda que de forma simples e empírica.
potencia x y = if (y==0) then 1 else x * potencia x (y-1)
Em Haskell, a razão, o argumento de convencimento, está implícito na própria definição da linguagem.
Observe que esta é uma chamada recursiva de uma relação recorrente.
A relação recorrente está matematicamente explicitada na chamada recursiva.
Frank Coelho de Alcantara - 2021
Funções Puras: produzem o mesmo resultado para a mesma entrada e não possuem nenhum efeito colateral.
Recursão: não usamos nenhum laço de repetição.
Funções de primeira-classe: podem ser passadas como parâmetro, devolvidas e armazenadas em estruturas de dado.
Variáveis imutáveis: você pode criar uma variável nova mas não pode mudar o valor de uma variável já criada.
Característica | Abordagem imperativa | Abordagem funcional |
---|---|---|
Foco do programador | Como executar tarefas (algoritmos) e como controlar alterações no estado. | Informações que é desejada e que transformações são necessárias. |
Alterações de estado | Importante. | Inexistente. |
Ordem de execução | Importante. | Baixa importância. |
Controle de fluxo primária | Loop, condições, e chamadas de função (método). | Chamadas de função, incluindo a recursão. |
Unidade principal de manipulação | Instâncias das classes ou estruturas. | Funções como objetos de primeira classe e coleções de dados. |
Disponível aqui.