Logica di Hoare: Nozioni Preliminari
La Logica di Hoare e' un sistema formale che si puo'
utilizzare per fornire la semantica di linguaggi di programmazione imperativi. {P} C {Q} dove
Le asserzioni non sono altro che formule ben formate della
logica dei predicati, le cui variabili libere sono variabili utilizzate
nel programma (ed eventualmente anche le locazioni di memoria), o nell'istruzione.
σ : Identificatori → Z
Notare come uno stato non sia altro che un ambiente
(nel senso della Definizione 4.6 del testo [SM]) per
la struttura corrispondente, nel caso del linguaggio WHILE,
ai numeri interi.
σ |= P
se l'asserzione P (che, ricordiamo, e' una formula ben formata della logica
dei predicati) e' soddisfatta nella struttura dei numeri interi (nel caso
del nostro linguaggio WHILE) rispetto all'ambiente corrispondente a σ (cioe', in notazione logica, [[P]]Zσ = 1).
σ |= P implica φC(σ) |= Q
cioe', per ogni stato σ che soddisfa la pre-condizione ed a partire dal quale l'esecuzione di C termina, σ
modificato dall'esecuzione di C soddisfa la post-condizione.
{X1 > 0 ∧ X2 > 0 ∧ Pari(X2)} begin X2 := succ(X2); X1:=0 end {(X1 = 0 ∨ X2 = 0) ∧ ¬(Pari(X2))} Esempio di uso della regola (iterazione)
Una volta studiato il testo [US], vediamo un po' in dettaglio un esempio
che in pratica e' la soluzione dell'esercizio 70 in [US].
{A ∧ E} I {A} ---------------------------------- {A} while E do I end {A ∧ ¬E}
Regola all'apparenza strana, poiche', per cicli while, permette
di asserire, al termine di un ciclo, praticamente solo formule
che sono anche pre-condizioni.
q := 0; r := a; while r ≥ b do q := q+1; r := r - b endw
Supponiamo di aver scritto questo frammento di codice (magari all'interno di
un programma piu' grande) poiche' sappiamo che all'inizio dell'esecuzione
di tale frammento il valore contenuto in a sara' maggiore
o uguale a zero, mentre quello di b maggiore di zero,
e perche' al termine vogliamo che la variabile q contenga
il quoziente della divisione intera tra a e b, mentre dentro r vogliamo che
ci sia il resto di tale divisione. A tale scopo infatti il nostro frammento
di codice implementa l'algoritmo della divisione intera per sottrazioni successive.
|- {a ≥ 0 ∧ b > 0} C {(a = b * q + r) ∧ (0 ≤ r < b)}
dove con C indichiamo il nostro frammento di codice.
{a ≥ 0 ∧ b > 0} q := 0; r := a; {r ≥ 0 ∧ b > 0 ∧ (a = b * q + r)}
utilizzando l'assioma per gli assegnamenti e la regola per la sequenza di comandi
(in realta' occorre anche utilizzare la regola che permette di rafforzare la
precondizione). {r ≥ 0 ∧ b > 0 ∧ (a = b * q + r)} C' { r ≥ 0 ∧ b > 0 ∧(a = b * q + r) ∧ ¬(r ≥ b)}
dove C' e' l'istruzione while del nostro codice.
r ≥ 0 ∧ b > 0 ∧ (a = b * q + r) e come condizione E ovviamente la formula r ≥ b. {r≥0 ∧ b>0 ∧ (a= b*q + r) ∧ r≥b} I {r≥0 ∧ b>0 ∧ (a= b*q + r)} --------------------------------------------------------------------- {r≥0 ∧ b>0 ∧ (a = b*q + r)} C' {r≥0 ∧ b>0 ∧ (a= b*q + r) ∧ ¬(r≥b)}
dove nel nostr caso I e' il frammento di codice q := q+1; r := r - b.
r ≥ 0 ∧ b > 0 ∧ (a = b * q + r) ∧ ¬(r ≥ b)
sia equivalente a (a = b * q + r) ∧ (0 ≤ r < b) Questo significa che la regola (iterazione) ci permette di derivare {r≥0 ∧ b>0 ∧ (a = b*q + r)} C' {(a = b * q + r) ∧ (0 ≤ r < b)} se siamo capaci di derivare {r≥0 ∧ b>0 ∧ (a= b*q + r) ∧ r≥b} q:=q+1; r:=r-b {r≥0 ∧ b>0 ∧ (a= b*q + r)} Questo ultimo giudizio si puo' dimostrare usando due volte la regola per l'assegnamento e la regola per la sequenza di istruzioni (farlo come esercizio!). A questo punto abbiamo quindi dimostrato che {a ≥ 0 ∧ b > 0} q := 0; r := a; {r ≥ 0 ∧ b > 0 ∧ (a = b * q + r)} e che {r≥0 ∧ b>0 ∧ (a = b*q + r)} C' {(a = b * q + r) ∧ (0 ≤ r < b)} Usando ora la regola per la sequenza di istruzioni possiamo derivare cio' che ci eravamo prefissi, cioe' {a ≥ 0 ∧ b > 0} C {(a = b * q + r) ∧ (0 ≤ r < b)} |