Semantica Assiomatica Anche nella semantica assiomatica, così come in quella operazionale, il significato associato ad un comando C viene definito specificando la transizione tra stati (a partire, cioè, da uno stato iniziale per arrivare a uno finale) generata da C. Ciò che contraddistingue, quindi, una particolare definizione semantica rispetto ad un altra è il formalismo scelto per definire un generico stato del sistema. Nell approccio assiomatico, lo stato è definito da un predicato logico che stabilisce l insieme degli enunciati che sono veri all interno del sistema in un determinato momento. Dati due predicati Q, R e un comando C, definiamo tripla di Hoare per il comando C con precondizione Q e postcondizione R, il predicato {Q} C {R} che risulta vero se e solo se l esecuzione del comando C a partire da uno stato in cui è vero il predicato Q produce uno stato in cui risulta vero il predicato R. Scrivere un programma in un qualsiasi linguaggio si riconduce al seguente schema di attività: dato un insieme di informazioni iniziali (input), determinare una sequenza di comandi che, una volta eseguiti, producano un risultato che soddisfi certe proprietà (output). Usando le triple di Hoare, dati due predicati Q e R, vogliamo determinare C tale che {Q} C {R} risulti vero a partire da ogni stato in cui Q è vera. Come determinare C? Un approccio più semplice dal quale partire è quello di risolvere il problema della verifica di un programma, ovvero dati Q, R e C, stabilire se {Q} C {R} risulta vero a partire da ogni stato in cui Q è vera. Questo problema si può ridurre ad un altro, più semplice, che consiste nel calcolare, a partire da C e R, un predicato W tale che {Q} C {R} sia vera se e solo se Q implica W. Il predicato W si chiama precondizione più debole (weakest precondition) di C rispetto a R e si indica con wp[c]r. La precondizione più debole risulta quindi così definita. Definizione. Per ogni predicato R e comando C, la precondizione più debole di C rispetto a R è il predicato wp[c]r tale che, per ogni predicato Q, vale che {Q} C {R} Q wp[c]r. Dalla definizione di precondizione più debole segue direttamente che, se si esegue C in uno stato in cui è vera wp[c]r, si arriva in uno stato in cui è vera la postcondizione R, come dimostrato nella seguente proposizione.
Proposizione1. Per ogni predicato R e comando C, vale {wp[c]r} C {R}. Dimostrazione. {wp[c]r} C {R} wp[c]r wp[c]r true. La precondizione più debole è dunque un predicato tale che, partendo da uno stato che lo soddisfa, l esecuzione di C porta in uno stato in cui R è soddisfatta. In particolare, possono esistere infiniti stati che verificano questa proprietà. La precondizione più debole identifica l insieme più ampio di stati a partire dai quali l esecuzione di C porta in uno stato che soddisfa R. Chiariamo questo concetto con un piccolo esempio. Consideriamo le due triple di Hoare: {c = 1 ٨ b = 4} a = c {a + b = 5} {c = 2 ٨ b = 3} a = c {a + b = 5} Come vedremo in seguito, si ha che wp[a = c](a + b = 5) c + b = 5. Abbiamo che c = 1 ٨ b = 4 c + b = 5, c = 2 ٨ b = 3 c + b = 5. Si può apprezzare come la condizione espressa dal predicato c + b = 5 catturi l insieme di tutti i possibili stati di partenza per i quali, dopo aver assegnato il valore c alla variabile a, si ottiene che a + b = 5. Diamo ora un insieme di postulati riguardanti le precondizioni più deboli e che assumiamo implicitamente veri. Postulato 1 (Congiuntività). Per ogni coppia di predicati Q, R e per ogni comando C si ha wp[c](q ٨ R) wp[c]q ٨ wp[c]r. Postulato 2 (Disgiuntività). Per ogni coppia di predicati Q, R e per ogni comando C si ha wp[c](q ٧ R) wp[c]q ٧ wp[c]r. Postulato 3 (Nessun Miracolo). Per ogni comando C si ha wp[c]false false. Siano S(C,Q) e S(C,R) l insieme degli stati a partire dai quali, dopo aver eseguito C, si raggiunge uno stato che soddisfa rispettivamente Q e R. Intuitivamente, il postulato 1 dice che l insieme degli stati dai quali, dopo aver eseguito C, si può raggiungere uno stato che soddisfi contemporaneamente Q e R equivale all intersezione di S(C,Q) e S(C,R); mentre il postulato 2 dice che l insieme degli stati dai quali, dopo aver eseguito C, si può raggiungere uno stato che soddisfi almeno uno tra Q e R equivale all unione tra S(C,Q) e S(C,R). Infine, il postulato 3 dice che, siccome nessuno stato può soddisfare il falso, l insieme più ampi degli stati che soddisfano il falso deve essere vuoto.
Le precondizioni più deboli verificano, tra le altre, le seguenti proprietà fondamentali. Proprietà 1 (Monotonicità). Per ogni coppia di predicati Q, R e per ogni comando C si ha (Q R) (wp[c]q wp[c]r). Dimostrazione. wp[c]q [sfruttando (Q R) Q Q ٨ R] wp[c](q ٨ R) [sfruttando Congiuntività] wp[c]q ٨ wp[c]r [sfruttando P ٨ Q Q] wp[c]r. Proprietà 2 (Regola della Postcondizione). Per ogni tripla di predicati Q, R, A e per ogni comando C si ha {Q} C {A} ٨ (A R) {Q} C {R}. Dimostrazione. {Q} C {A} [per definizione di wp] Q wp[c]a [sfruttando A R e Monotonicità] Q wp[c]a wp[c]r [sfruttando la transitività di ] Q wp[c]r {Q} C {R}. Proprietà 3 (Regola della Precondizione). Per ogni tripla di predicati Q, R, A e per ogni comando C si ha {A} C {R} ٨ (Q A) {Q} C {R}. Dimostrazione. {A} C {R} [per definizione di wp] A wp[c]r [sfruttando Q A] Q A wp[c]r [sfruttando la transitività di ] Q wp[c]r {Q} C {R}. Semantica dei Comandi Specifichiamo ora le precondizioni più deboli associate ai comandi del nostro mini linguaggio di riferimento. 1. wp[skip]r R 2. wp[c 1 ;c 2 ]R wp[c 1 ](wp[c 2 ]R) Vediamo come il comando skip si comporti da operatore unitario nell ambito della sequenza di comandi. wp[c;skip]r [wp della sequenza di comandi] wp[c](wp[skip]r) [wp di skip] wp[c]r [wp di skip] wp[skip](wp[c]r) [wp della sequenza di comandi] wp[skip;c]r.
Data un espressione a AExp BExp, introduciamo un predicato Def[a] che risulta vero se e solo se l espressione a è ben definita. In particolare, ricordando la natura del nostro linguaggio di riferimento, ci preoccuperemo di verificare che l operazione di differenza tra due termini sia sempre tale che il minuendo sia maggiore uguale del sottraendo. 3. wp[x = a]r R a X ٨ Def[a] 4. wp[if b then c 1 else c 2 ]R Def[b] ٨ ((b ٨ wp[c 1 ]R ) ٧ ( b ٨ wp[c 2 ]R)) Vediamo un esempio di applicazione della precondizione più debole relativa al commando condizionale. wp[if X 1 X 2 then X 3 = X 1 else X 3 = X 2 ](X 3 = max{x 1 ;X 2 }) Def[X 1 X 2 ] ٨ ((X 1 X 2 ٨ wp[x 3 = X 1 ] (X 3 = max{x 1 ;X 2 })) ٧ (X 1 < X 2 ٨ wp[x 3 = X 2 ] (X 3 = max{x 1 ;X 2 }))) (X 1 X 2 ٨ wp[x 3 = X 1 ] (X 3 = max{x 1 ;X 2 })) ٧ (X 1 < X 2 ٨ wp[x 3 = X 2 ] (X 3 = max{x 1 ;X 2 })) (X 1 X 2 ٨ X 1 = max{x 1 ;X 2 }) ٧ (X 1 < X 2 ٨ X 2 = max{x 1 ;X 2 }) X 1 X 2 ٧ X 1 < X 2 True. Quindi, tutti i possibili stati iniziali soddisfano la postcondizione, indipendentemente dai valori di X 1 e X 2. Per quanto riguarda il ciclo while, il generico predicato che definisce la sua wp è così complesso da renderlo preticamente inutilizzabile in pratica. Saremo costretti, quindi, ad utilizzare il risultato di un teorema la cui applicazione, però, richiede l invenzione, da parte del programmatore, di una formula (detta invariante) che risulti sempre vera durante l intera esecuzione del while. Questa situazione riflette esattamente il fatto che la creazione di cicli risulta l attività più complessa nell ambito della programmazione imperativa. Sia t una funzione a valori interi e INV un predicato, enunciamo il seguente teorema. Teorema di iterazione finita. {INV ٨ E} C {INV ٨ Def[E]} (invarianza) ٨ {INV ٨ E ٨ t = T} C {t < T} (progresso) ٨ INV ٨ E t > 0 (terminazione) {INV ٨ Def[E]} while E do C {INV ٨ E}.
Il teorema afferma che nelle tre ipotesi di invarianza, progresso e terminazione, allora INV ٨ Def[E] e INV ٨ E possono essere assunte come precondizione e postcondizione del while. Per poter risolvere il problema di costruire un while che soddisfi la tripla {P} while E do C {R}, bisogna quindi trovare INV e t che verifichino le ipotesi del teorema, con INV tale che P INV ٨ Def[E] e INV ٨ E R. Questo per le regole della precondizione e della postcondizione. Vediamo ora un esempio. Voglio scrivere un programma che dati due numeri interi non negativi A e B li sommi e memorizzi il risultato in una variabile di nome z. Risolviamo il problema attraverso il seguente programma P: z = A; u = B; while (u!= 0) do {z = z + 1; u = u - 1;} Verifichiamo la correttezza formale del nostro programma provando che la tripla di Hoare {A 0 ٨ B 0} P {z = A + B} è vera. Per prima cosa dobbiamo risolvere il problema del ciclo while. Definiamo come funzione t il valore della variabile u e come invariante INV il predicato z + u = A + B ٨ u 0. Mostriamo che soddisfano le condizioni di invarianza, progresso e terminazione. Per la condizione di invarianza abbiamo: wp[z = z + 1; u = u 1](z + u = A + B ٨ u 0) wp[z = z + 1]((z + u = A + B ٨ u 0) u-1 u ٨ Def[u - 1]) wp[z = z + 1](z + u = A + B + 1 ٨ u 1) (z + u = A + B + 1 ٨ u 1) z+1 z ٨ Def[z+1] z + u = A + B ٨ u 1 INV ٨ u 0 INV ٨ E Per la condizione di progresso abbiamo: wp[z = z + 1; u = u 1](u < T) wp[z = z + 1]((u < T + 1) u-1 u ٨ Def[u - 1]) wp[z = z + 1+](u < T + 1 ٨ u 1) u < T + 1 ٨ u 1 INV ٨ E ٨ t = T
La condizione di terminazione, infine, è banalmente implicata da E. Dato, inoltre, che INV ٨ E implica z = A + B, per la regola della postcondizione possiamo concludere che la tripla di Hoare {z + u = A + B ٨ u 0} while (u!= 0) do {z = z + 1; u = u - 1;} {z = A + B} è vera. Procedendo con il calcolo abbiamo che wp[z = A; u = B](z + u = A + B ٨ u 0) wp[z = A](z + u = A + B ٨ u 0) B u ٨ Def[B] wp[z = A](z = A ٨ B 0) (z = A ٨ B 0) A z ٨ Def[A] A 0 ٨ B 0 che equivale all input del programma P. Abbiamo quindi dimostrato che P è corretto.