Semântica Axiomática
O estilo axiomático presta-se particularmente à prova e raciocínio sobre propriedades dos programas, e à sua verificação, i.e. à prova de correcção dos programas face às suas especificações. A ferramenta semântica que estudaremos é a Lógica de Floyd-Hoare
Correcção Parcial A Lógica de Floyd-Hoare lida com triplos de correcção parcial da forma {P} C {Q} em que C é um programa e P e Q são propriedades do programa. P designa-se por pré-condição e Q por pós-condição.
Correcção Parcial Significado de {P} C {Q}: se C termina quando executado a partir de um estado S em que P se verifica, então, se C terminar, Q verifica-se. O que esta asserção não especifica: que C termine a partir do estado S o que acontece se C não terminar daí a parcialidade desta noção de correcção.
Linguagem Lógica {P} C {Q} Qual a sintaxe e poder expressivo da linguagem utilizada para descrever as propriedades P e Q? A linguagem lógica deverá conter pelo menos as conectivas disponíveis na linguagem em estudo.
mas precisaremos agora de conectivas lógicas como conjunção, disjunção, e negação: L ::= b E bop E L lop L lop {,,, } Asserções poderão ainda conter quantificadores,
Regras de Inferência para a Correcção Parcial {P }skip{p } {P }C 1 {Q} {Q}C 2 {R} {P }C 1 ; C 2 {R}
{P B}C 1 {Q} {P B}C 2 {Q} {P }if B then C 1 else C 2 {Q} {Q[E/l]} l := E {Q}
Invariantes de ciclo Uma propriedade P diz-se um invariante do ciclo while B do C se puder ser inferido. {P B}C{P } A que se deve a presença de B na pré-condição?
Note-se que se o ciclo while B do C terminar, P é verdadeiro depois da sua execução, e B só pode ser falso. {P B}C{P } {P }while B do C{P B} Porque não se inclui B na pré-condição?
Regras Lógicas: Consequência P = P {P }C{Q } Q = Q {P }C{Q} Permite fortalecer a pré-condição e/ou enfraquecer a pós-condição
Correcção da Semântica Axiomática Dados um comando C e predicados P e Q tais que o triplo de correcção parcial seguinte {P }C{Q} é derivável na Lógica de Hoare, seja s um estado que satisfaz P, e C [C ](s) = s., Então s satisfaz Q.
Interpretação de Asserções A semântica de uma linguagem lógica de predicados é bem conhecida de outras disciplinas. Identificando-se estados e valorações, a interpretação das asserções será uma extensão da interpretação das expressões booleanas da linguagem. Exemplo: [!l 1 =!l 2 ]s = (E [!l 1 ](s) = E [!l 2 ](s))
Interpr. de Triplos de Hoare [{P }C{Q}] = true se para todos os estados s e s tais que [P ](s) = true e C [C ](s) = s se tem [Q](s ) = true. [{P }C{Q}] = false em todos os outros casos
Correcção da Semântica Axiomática (v.2) Dados um comando C e predicados P e Q tais que o triplo de correcção parcial seguinte {P }C{Q} é derivável na Lógica de Hoare, então [{P }C{Q}] = true. (prova indutiva nas regras da Lógica de Hoare)
Lógica de Floyd-Hoare Complementos
Asserções Especiais Naturalmente, para uma dada pós-condição Q, temos interesse em saber qual a pré-condição mais fraca que garante essa pós-condição, uma vez que ela pode depois ser fortalecida. Caso extremo: {true} C {Q} significa que sempre que C termina, Q é satisfeito. A pré-condição é a mais fraca possível.
Asserções Especiais Simetricamente, para uma pré-condição P temos interesse em conhecer a pós-condição mais forte por ela garantida, que pode depois ser enfraquecida. Caso extremo {P} C {false} quando executado a partir de um estado em que P se verifica, C não termina. A pós-condição é a mais forte possível.
Asserções Especiais {P} C {true} e {false} C {Q} Asserções axiomaticamente válidas, correspondem à pós-condição mais fraca possível, e à pré-condição mais forte possível
Variáveis Auxiliares É útil poder referir numa pós-condição o valor que uma variável tinha no início do programa. Por exemplo para especificar o comportamento de um programa swap que troca os valores de duas variáveis: {!l1=x!l2=y} swap {!l1=y!l2=x} As variáveis auxiliares x e y permitem exprimir isto. Ex: escrever swap e provar a validade da asserção
Especificações Complexas A especificação de programas com alguma complexidade leva-nos à introdução de predicados e/ou funções na linguagem de asserções. Um exemplo típico é o do cálculo de uma grandeza matemática definida recursivamente, como a exponenciação: exp (n,0) = 1 exp (n,k+1) = n * exp (n, k)
{!l 2 0} calcexp {!l 3 = exp (!l 1,!l 2 )} Exercício: escrever calcexp e provar a sua correcção PROBLEMA: existe uma solução trivial! A especificação acima deve ser corrigida
{!l 2 0} calcexp {!l 3 = exp (!l 1,!l 2 )} Exercício: escrever calcexp e provar a sua correcção PROBLEMA: existe uma solução trivial! A especificação acima deve ser corrigida: {!l1=x!l2=y!l2 0} calcexp {!l 3 = exp (!l 1,!l 2 )!l 1 =x!l 2 =y}
Correcção Total Questão: quais as dificuldades inerentes ao tratamento de uma noção de correcção total? A única forma de se definir uma regra de inferência para lidar com a terminação de um ciclo é através da introdução de um variante de ciclo uma grandeza numérica não-negativa e decrescente em todas as iterações do ciclo.
Correcção Total As asserções de correcção total escrevem-se normalmente como [P] C [Q] [P B V=n] C [P V<n] P B V 0 [P] while B do C [P B] As restantes regras são iguais às de correcção parcial.
Se já se provou a correcção parcial, para provar a correcção total basta provar que o programa termina, ou seja [P] C [true] Exercício: Provar a correcção total do programa que calcula a exponenciação