Regras da Verificação de Programas
As regras da verificação de programas estão organizadas (essencialmente) pelos tipos de instruções.
Ciclos e Programas Parciais
Num triplo de Hoare, , a verificação da pós-condição pressupõe que termina quando .
Entre os tipos de instruções, apenas os ciclos podem contribuir para programas que não terminam; Um programa apenas com instruções "cópia" ou "condição" sem ciclos termina sempre.
Como o problema da paragem é indecidível, isto é, não há um algoritmo para classificar um programa como total ou parcial, as regras de verificação que envolvem ciclos (onde está o "problema" da paragem) devem contemplar ambos os casos.
De facto, existe uma regra para ciclos totais e outra regra para ciclos parciais.
O tratamento dos ciclos totais está fora do âmbito desta disciplina e vai-se usar apenas a regra do caso parcial nos ciclos.
A razão desta escolha está na complexidade adicional de tratar o caso total. Os ciclos, como estão apresentados, permitem programas parciais e, claro, também programas totais.
Uma forma de garantir que um ciclo termina consiste em usar ciclos limitados. Por exemplo, a sintaxe do C
permite
for (int i = 0; i < 10; i++) {
// instruções que não afetam i
}
Claro, isto é um caso particular de ciclos ilimitados:
#![allow(unused)] fn main() { i = 0; while i < 10 { // instruções que não afetam i i = i + 1; } }
Regras para a Verificação de Programas Parciais
Implicação
Definição ()
Nesta regra:
- são fórmulas FOL+Aritmética.
- significa que existe uma prova de com hipóteses na lógica FOL+Aritmética.
P
é um programa.
Esta é uma regra de "conveniência", que permite generalizar a pré-condição e restringir a pós-condição.
Sequência
Sequência ()
Nesta regra:
- são fórmulas FOL+Aritmética.
P
eQ
são programas.
Intuitivamente:
- Se antes de e depois,
- e se antes de e depois;
- Estão, quando antes de também depois:
Cópia
Cópia ()
Nesta regra:
- é uma fórmula FOL+Aritmética.
- é um termo aritmético.
- resulta de substituir, em , algumas ocorrências da variável por .
Exemplo. Provas em Tabela
Mostrar que
Temos de usar a regra
Para esta regra ser corretamente aplicada é necessário identificar o termo e a fórmula .
Uma prova em tabela é:
Linha | Instrução | Fórmula | Regra | Hipóteses | Observação |
---|---|---|---|---|---|
1 | prec | pré-condição | |||
2 | aritm | 1 | aritmética na linha anterior | ||
3 | x = y + 1 | ||||
4 | copy | 2, 3 | é a fórmula "" |
- Na primeira linha está a pré-condição do triplo (prec) e na última linha está a pós-condição do triplo.
- A segunda linha resulta da primeira por aplicação das regras da aritmética (aritm).
- Na terceira linha está (apenas) a instrução do triplo.
- Finalmente, a última linha resulta de aplicar a regra .
Mas ficam algumas dúvidas:
- Qual a justificação para a linha 2? É necessária?
- Donde vêm a fórmula e o termo ? O que se passa com a substituição ?
Como identificar a fórmula e o termo usados na regra .
- De acordo com a regra, deve ser a pós-condição de uma instrução da forma
x = E
. - O que pretendemos com esta regra é obter a fórmula . Portanto, deve ser .
- Falta o . A instrução "real" é
x = y + 1
. Portanto, deve ser . - Isso significa que na prova deve estar a fórmula , que resulta de .
- Por isso é usado o teorema da aritmética e a regra .
- Em casos diretos, como este, pode usar-se a "pseudo-regra" para simplificar a prova.
Mais rigorosamente, o que aquela tabela apresenta é a seguinte prova formal:
Este exemplo ilustra a seguinte eurística para as provas de verificação:
As linhas de uma prova lêem-se "de cima para baixo" mas escrevem-se "de fora para dentro"
O que isto significa é que as seguintes regras ajudam a fazer uma prova:
- Começar por escrever as hipóteses ou pré-condições dadas e a conclusão ou pós-condição pretendida.
- "Descer" a partir das linhas superiores enquanto as conclusões são diretas.
- "Subir" das linhas inferiores enquanto a hipóteses necessárias são diretas.
Exemplos da regra .
Condicional
Condicional ()
Nesta regra:
- e são fórmulas FOL + Aritmética.
P
eQ
são programas, respetivamente os ramos positivo e negativo da instrução condicional.
Exemplo.
Seja Max
o programa
#![allow(unused)] fn main() { if x > y { z = x } else { z = y } }
Recapitulando a definição de da aritmética:
L. | Instrução | Fórmula | Regra | Hipóteses | Observação |
---|---|---|---|---|---|
1 | prec | ||||
2 | if x > y { | ||||
? | |||||
? | z = x | ||||
? | |||||
? | } else { | ||||
? | |||||
? | z = y | ||||
? | |||||
? | } | ||||
? | cond | 2, ? - ?, ? - ? |
L. | Instrução | Fórmula | Regra | Hipóteses | Observação |
---|---|---|---|---|---|
1 | prec | ||||
2 | if x > y { | ||||
? | |||||
? | z = x | ||||
? | copy | têm de ser iguais | |||
? | } else { | ||||
? | |||||
? | z = y | ||||
? | copy | têm de ser iguais | |||
? | } | ||||
? | cond | 2, ? - ?, ? - ? | têm de ser iguais |
L. | Instrução | Fórmula | Regra | Hipóteses | Observação |
---|---|---|---|---|---|
1 | prec | ||||
2 | if x > y { | ||||
3 | posb | guarda | |||
? | z = x | ||||
? | copy | ||||
? | } else { | ||||
8 | negb | negação da guarda | |||
? | z = y | ||||
? | copy | ||||
? | } | ||||
? | cond | 2, ? - ?, ? - ? |
L. | Instrução | Fórmula | Regra | Hipóteses | Observação |
---|---|---|---|---|---|
1 | prec | ||||
2 | if x > y { | ||||
3 | posb | ||||
4 | ? | ||||
5 | z = x | ||||
6 | copy | ||||
7 | } else { | ||||
8 | negb | negação da guarda | |||
? | z = y | ||||
? | copy | ||||
? | } | ||||
? | cond | 2, ? - ?, ? - ? |
L. | Instrução | Fórmula | Regra | Hipóteses | Observação |
---|---|---|---|---|---|
1 | prec | ||||
2 | if x > y { | ||||
3 | posb | ||||
4 | impl | ||||
5 | z = x | ||||
6 | copy | ||||
7 | } else { | ||||
8 | negb | ||||
9 | impl | ||||
10 | z = y | ||||
11 | copy | ||||
12 | } | ||||
13 | cond | 2, ? - ?, ? - ? |
L. | Instrução | Fórmula | Regra | Hipóteses | Observação |
---|---|---|---|---|---|
1 | prec | ||||
2 | if x > y { | ||||
3 | posb | ||||
4 | z = x | ||||
5 | copy | ||||
6 | impl | ||||
7 | } else { | ||||
8 | negb | ||||
9 | z = y | ||||
10 | copy | ||||
11 | impl | ||||
12 | } | ||||
13 | cond | 2, 3 - 6, 8 - 11 |
Ciclo Parcial e Invariante
Definição (, Invariante)
Uma invariante do ciclo é qualquer fórmula tal que
Isto é,
A fórmula é um invariante do ciclo se para qualquer estado tal que , quando é executado no estado e termina, no estado final, , ainda .
Por outras palavras, um invariante de um ciclo é uma condição que é válida antes e depois da computação do corpo do ciclo. Pode acontecer que durante essa computação a condição deixe de ser válida desde que, no fim, volte a sê-lo.
Exemplo. Verificação de um programa para calcular .
Mostre que em que Fac1
é o programa
#![allow(unused)] fn main() { y = 1; z = 0; while z != x { z = z + 1; y = y * z; } }
Para este exemplo é preciso recapitular a seguinte propriedade da aritmética:
L. | Instrução | Fórmula | Regra | Hipóteses | Observação |
---|---|---|---|---|---|
1 | prec | ||||
2 | if x > y { | ||||
3 | posb | ||||
4 | z = x | ||||
5 | copy | ||||
6 | impl | ||||
7 | } else { | ||||
8 | negb | ||||
9 | z = y | ||||
10 | copy | ||||
11 | impl | ||||
12 | } | ||||
13 | cond | 2, 3 - 6, 8 - 11 |