La Logica di Hoare - Dipartimento di Matematica e Informatica

SSiS Veneto
Fondamenti s.e. dell’Informatica I
Note del corso
A.A. 2001/2002
Ugo Solitro
0
U. Solitro
Università degli Studi di Verona
Dipartimento di Informatica
Strada le Grazie, 15 – 37134 Verona
telefono +39 045 802-79.77,
e.mail [email protected]
CAPITOLO 5
La logica di Hoare
Dopo aver affrontato la questione generale della correttezza, inquadrandola matematicamente e accennato ai problemi generali legati alle dimostrazioni di correttezza, proponiamo in questo capitolo una tecnica per le dimostrazioni di correttezza parziale che usa le
tecniche proprie della teoria della dimostrazione.
Alla luce dei principali risultati di indecidibilità che vengono della teoria matematica
della computabilità, sappiamo che nessuna metodologia generale è in grado di dare una
soluzione definitiva alle suddette questioni. Il sistema formale che qui presenteremo è
ugualmente importante perché da una parte mostra, nel caso ce ne fosse bisogno, come
strumenti di tipo logico-matematico possono fornire l’ambiente adeguato per la trattazione
delle questioni legate alla correttezza e perché consente di dare una semantica alternativa
(nota come semantica assiomatica, in un certo senso più elementare al nostro linguaggio
di programmazione imperativo.
1. Sistemi deduttivi formalizzati
Un sistema deduttivo1 è sostanzialmente un modo di presentare una teoria matematica
per mezzo di assiomi e regole. Esempi notevoli di sistemi deduttivi sono la Geometria di
Euclide, l’Aritmetica di Peano, la Meccanica Razionale e molte altre ancora. In tutti i
casi citati si tratta di teorie matematiche delle quali sono noti (l’insieme de) gli assiomi e
(l’insieme de) le regole per dedurre asserzioni vere della teoria.
Si noti che una stessa teoria matematica può esser presentata in molti modi diversi!
Prendiamo ad esempio la teoria degli insiemi; è noto che la prima presentazione (o assiomatizzazione) della teoria, quella di Cantor, è insoddisfacente, furtunatamente ci sono
presentazioni alternative: fra le più famose citiamo quelle note come teoria di ZermeloFraenkel (zf) e teoria di Gödel-Bernays; ambedue sono di fatto sistemi deduttivi per la
stessa teoria matematica, ma differiscono nella scelta degli assiomi e delle regole2.
Per l’appunto la stessa logica matematica nelle sue differenti formulazioni può esser
descritta per mezzo di assiomi e regole di deduzione.
1.1. Regole di Inferenza. Quello che a noi qui interessa è che tali sistemi deduttivi
possono esser “formalizzati”, cioè possono essere descritti per mezzo di opportuno linguaggio formale, fatto questo che riveste una certa importanza dal momento che ci occupiamo
di linguaggi formalizzati per scrivere algoritmi.
Per capire meglio di cosa stiamo parlando cominciamo con qualche esempio concreto.
Esempio 30. Teoria dell’uguaglianza. Vogliamo descrivere le proprietà fondamentali dell’uguaglianza per mezzo di un sistema deduttivo formalizzato. Per fissare le
idee possiamo assumere di voler trattare espressioni del linguaggio while.
1
Questo è campo di studi per la Logica Matematica e più precisamente per la Teoria della
Dimostrazione (Proof Theory).
2
Un analisi più profonda delle due assiomatizzazioni rivela differenze tecnicamente apprezzabili, sulle
quali tuttavia non ci possiamo soffermare.
45
46
5. LA LOGICA DI HOARE
Ecco le ben note proprietà dell’uguaglianza
riflessiva:
t = t,
simmetrica:
t = t0 ⇒ t0 = t,
transitiva:
t = t0 , t0 = t00 ⇒ t = t00 .
La prima proprietà può esser vista come un assioma che asserisce ogni espressione è uguale
a sé stessa, mentre le altre due possono esser lette come regole che permettono di dedurre
una certa conclusione ogni volta che siano soddisfatte le premesse.
Una regola di inferenza con premesse A1 , . . . , An e conclusione A si può scrivere in
può essere scritta in modo graficamente efficace nel seguente modo:
A1 , . . . , A n
A
Esempio 31. (continuazione)
Le proprietà dell’uguaglianza possono esser descritte dalle regole formalizzate seguenti
x=y
x=y
y=z
x=x
y=x
x=z
alle quali però bisogna aggiungere un’ulteriore regola che consenta di costruire espressioni
uguali non banali a partire da espressioni più semplici:
t1 = t01 . . . tn = t0n
f (t1 , . . . , tn ) = f (t01 , . . . , t0n )
(Esercizio: che differenza c’è – a parte la forma grafica – tra la prima formulazione delle
proprietà dell’uguaglianza e quella formalizzata?
Esercizio 63. Estendere il sistema deduttivo dell’ugualianza introducendo alcune
regole per il calcolo della somma e il prodotto.
Esercizio 64. Abbiamo già visto che un linguaggio formalizzato può esser per mezzo
di una grammatica bnf; ma è possibile (e in alcune situazioni utile) presentarlo come
sistema deduttivo.
Ad esempio, se volessimo descrivere le formule della logica proposizionale ptremmo
procedere nel che segue.
• Sia P = {pi | i ∈ N} un insieme numerabile di variabili proposizionali;
• Siano →, ∨, ∧ simboli per operazioni (logiche) binarie e ¬ un simbolo per un’operazione logica unaria;
• L’insieme F delle formule del calcolo proposizionale son tutte e sole quelle “deducibili” con le regole seguenti:
p∈P
A∈P
p∈F
A, B ∈ F
¬A ∈ F
A, B ∈ F
A, B ∈ F
A→B∈F
A∨B ∈F
A∧B ∈F
dove con A e B si indica una generica formula proposizionale.
(1) Scrivere un sistema deduttivo per la scrittura di programmi in un qualche senso
“corretti”.
(2) Esiste un procedimento uniforme per trasformare una grammatica bnf in un
sistema deduttivo? Quale è l’utilità di un tale procedimento?
1. SISTEMI DEDUTTIVI FORMALIZZATI
47
1.2. Il sistema di Deduzione Naturale. Un sistema deduttivo che si rispetti deve
esser in grado di usare le regole fondamentali della logica; ad esempio, la regola di inferenza
che permette di far uso dei lemmi può esser scritta in forma di regola di inferenza:
A
A→B
(Modus Ponens)
B
Un’altra regola interessante è quella che permette di dedurre un’implicazione. Intuitivamente (senza necessariamente pensare alle tabelle di verità) per dimostrare un’asserzione
del tipo A → B è necessario esibire una deduzione della conclusione B a partire dall’ipotesi A; questo è quello che normalmente accade in matematica, a meno che non si faccia
ricorso alla riduzione all’assurdo.
Questa situazione può esser cosı̀ schematizzata dalla seguente regola
[A]
..
.
B
A→B
che intuitivamente si legge:
se assumendo A si deduce B, si è dimostrato A → B indipendentemente
dalla “verità” dell’ipotesi A.
Questo tipo di regola è a volte chiamata regola di deduzione perché pone delle
condizioni o descrive degli effetti sull’intera deduzione3. In particolare si dirà l’assunzione
A è chiusa dalla regola per l’implicazione appena descritta; le assunzioni che non sono
chiuse da nessuna regola verranno per contrasto dette aperte.
È possibile descrivere la logica proposizionale (e predicativa) con un sistema deduttivo
che abbia al massimo tre regole per connettivo (operazione logica). Nella Tabella 1 è
rappresentato il sistema deduttivo completo4 per la logica proposizionale. Si noti che si
tratta di un sistema deduttivo privo di assiomi!
Osservazione 13. Dimostrazioni, Dimostrabilità e Verità (interludio).
Abbiamo introdotto in fretta e furia i sistemi deduttivi e mostrato un sistema deduttivo
per la logica proposizionale; tale sistema può esser esteso alle logiche di ordine superiore,
cominciando dalla logica dei predicati (I ordine). In questi sistemi deduttivi si possono
dimostrare teoremi senza far riferimento ad alcun concetto di “verità”, apparentemente
riducendo le dimostrazioni matematiche rappresentatabili in tali sistemi ad una manipolazione di simboli a livello sintattico. È possibile introdurre un concetto di modello e
quindi definire che cosa si intende per verità. Uno studio approfondito potrebbe mostrare
che verità e dimostrabilità non vanno sempre d’accordo5, ma questo va oltre gli limiti di
queste note.
Definizione 20. Sia D un sistema deduttivo per espressioni (o formule) in un linguaggio L. Diremo che A è deducibile dall’insieme di assunzioni Γ, e scriveremo
Γ ` A,
3
Tecnicamente le regole di inferenza, a differenza di quelle dette di deduzione, sono regole locali
perché si possono applicare senza sapere nulla dell’intera deduzione.
4 Il sistema in questione è noto come Deduzione Naturale e si deve la sua prima formulazione a
Gentzen [Gen69], ma è stato studiato più a fondo solo nella seconda metà del XX secolo a partire da
Prawitz [?].
5 Ricordiamo per tutti il caso dell’Aritmetica di Peano e i teoremi di Gödel [G8̈6].
48
5. LA LOGICA DI HOARE
[A]
..
.
A
B
A→B
A
A→B
B
B
A∧B
A
B
A∨B
A∨B
A∧B
A∧B
A
B
..
.
A∨B
[A]
..
.
[B]
..
.
C
C
C
¬A
..
.
A
..
.
⊥
⊥
⊥
¬A
A
A
Tabella 1. Sistema di Deduzione Naturale proposizionale.
se esiste una deduzione in D di A le cui assunzioni aperte sono un sottoinsieme di Γ.
Inoltre diremo che A è dimostrabile in D se ∅ ` A; in tal caso scriveremo
` A
Le asserzioni formali corrispondenti ai concetti appena definiti sono dette giudizi.
2. La logica dei programmi.
Finalmente arriviamo al sistema deduttivo per i programmi, cioè la cosiddetta Logica
di Hoare. Vogliamo costruire un sistema logico-deduttivo a partire dalle asserzioni di correttezza. Illustreremo alcuni casi semplici e da questi estrapoleremo un sistema deduttivo
che sarà tutto sommato piuttosto semplice.
Nel capitolo precedente avevamo introdotto le asserzioni di correttezza e ne avevamo
fissato il significato per via semantica: in particolare avevamo detto che {P } C {Q} è
valida (cioè soddisfatta in ogni stato) se per ogni stato σ
σ |= P
⇒
φC (σ) |= Q
L’idea è quella di scoprire se ci sono asserzioni che possano giocare il ruolo di assioma
e se è possibile fissare regole semplici, ma allo stesso tempo efficaci, che permettano di
costruire significative asserzioni di correttezza per induzione sulla struttura dell’algoritmo.
Definizione 21. Una regola
A1 . . . An
A
2. LA LOGICA DEI PROGRAMMI.
49
si dice corretta se conserva la validità, cioè se
|= A1 . . .
|= An
⇒
|= A
2.1. Il comando skip. Qual è un’asserzione di correttezza vera per il comando skip
con premesse minime? Chiaramente se prima dell’esecuzione del comando è valida una
certa proprietà A la stessa vale in ogni caso anche dopo; possiamo descrivere questo fatto
mediante una regola senza premesse (assioma)
(skip)
{A} skip {A}
qualunque sia A.
La regola è giustificata semanticamente perché
|= {A} skip {A}.
Esercizio 65. Dimostrare che la per il comando skip regola è corretta.
La regola è sufficiente per trattare il comando skip in tutte le situazioni? Di sicuro è
abbastanza espressiva, ma ci sono situazioni che non riusciamo a catturare; ad esempio,
non permette di dimostrare asserzioni del tipo {false} skip {A} che sono sicuramente
validi. Questo problema si ripresenta anche per le altre regole che introdurremmo, e lo
risolveremo alla fine.
2.2. L’assegnamento. Consideriamo il seguente comando di assegnamento
x := E
e assumiamo che il valore di E sia definito. Se prima dell’esecuzione del comando vale A
che cosa possiamo dire dopo la sua esecuzione? La locazione x ha cambiato il suo valore,
ma come si può esprimere questo fatto?
Stranamente è un poco più semplice “pensare a rovescio”: qual è la precondizione
minima che mi consente di dire che dopo l’esecuzione del comando di assegnamento vale A?
Non è difficile convincersi se prima dell’assegnamento vale la proprietà A(E/x) (l’asserzione
ottenuta sostituendo il valore di E per ogni occorrenza di x) allora A vale dopo; ad esempio,
sono valide le seguenti asserzioni:
{E = 5} x := E {x = 5},
{E < t} x := E {x < t},
{E = y} x := E {x = y}.
e, più in generale, {A(E)} x := E {A(x)}.
Esercizio 66. Dimostrare la validità delle asserzioni per esercizio.
Dunque possiamo formulare una nuova regola senza premesse:
(assegnamento)
{A(E/x)} x := E {A}
Esercizio 67. Dimostrare che la regola di assegnamento è corretta:
|= {A(E/x)} x := E {A}
2.3. Composizione sequenziale. Si tratta ora di fornire regole anche per i casi
in cui i comandi siano strutturati, cioè commposti di comandi più elementari. Cominciamo dalla composizione sequenziale dei comandi; si tratta di trovare una regola corretta,
cioè che conservi la validità delle asserzioni di correttezza; trattandosi questa volta di un
comando strutturato, le premesse non saranno vuote:
{A} I1 {B}
{B} I2 {C}
(composizione)
{A} I1 ; I2 {C}
La regola è abbastanza intuitiva, ma si siggerisce lo stesso di verificarne la correttezza
per esercizio.
50
5. LA LOGICA DI HOARE
2.4. Condizionale. Il caso del comando condizionale è appena un poco più complicato perché la condizione di verità della condizione determina l’esecuzione di comandi
diversi che danno origine in linea di principio a stati diversi.
(condizionale)
{A ∧ E} I1 {B}
{B ∧ ¬E} I2 {B}
{A} if E then I1 else I2 fi {B}
La regola appare paradossale ad un prima lettura: sembra infatti che, indipendentemente dalla valutazione della “guardia” E il risultato, descritto dalla asserzione B, si a
sempre lo stesso. In realtà dobbiamo ricordare che B è “semplicemente” una asserzione
nella logica dei predicati e, di conseguenza, può esser abbastanza espressiva da descrivere
correttamente la postcondizione.
Esercizio 68. Dimostrare che il seguente comando è parzialmente corretto:
{ x <> 0 }
if x>0 then y := 1
else y := -1
{ x>0 => y = 1, x<0 => y=-1 }
Suggerimento: dimostrare la validità della corrispondente asserzione di correttezza.
Provare inoltre ad istanziare la regola deduttiva corrispondente in modo da ottenere
come conclusione esattamente l’asserzione di correttezza necessaria alla dimostrazione di
correttezza parziale.
Se siete riusciti a svolgere completamente l’esercizio precedente vi sarete anche convinti
che la regola non adeguata a descrivere gli effetti del condizionale.
Esercizio 69. Dimostrare che la regola di inferenza del condizionale è corretta.
2.5. Iterazione. Il comando di iterazione è il più delicato (l’abbiamo già osservato
nei capitoli precedenti) perché in questo caso non sappiamo quante volte il comando verrà
iterato.
La soluzione è rappresentata da una regola che coinvolga una proprietà in grado di
descrivere adeguatamente il comportamento dell’iterazione in ogni momento:
{A ∧ E} I {A}
(iterazione)
{A} while E do I end {A ∧ ¬E}
Anche in questo caso inizialmente l’intuizione non aiuta un gran ché! Un esercizio può
anche in questo caso aiutare.
Esercizio 70. Si consideri il seguente algoritmo (che calcola la divisione intera):
{ a >= 0, b > 0 }
q := 0;
r := a;
{ a = q*b + r, r >= 0 }
while r => b do
r := r-b;
q := q+1
end
{ a = q*b + r, 0 <= r < b }
2. LA LOGICA DEI PROGRAMMI.
51
{A} skip {A}
{A} I1 {B}
{A(E/x)} x := E {A}
{A ∧ E} I1 {B}
{A} I1 ; I2 {C}
{B ∧ ¬E} I2 {B}
{A ∧ E} I {A}
{A} if E then I1 else I2 fi {B}
A → A0
{B} I2 {C}
{A} while E do I end {A ∧ ¬E}
{A0 } C {B}
B0 → B
{A} C {B}
{A} C {B 0 }
{A} C {B}
Tabella 2. La logica di Hoare.
• dimostrarne la correttezza parziale;
• Quale proprietà può essere messa al posto di A nella regola dell’iterazione?
La proprietà A che compare nella regola diinferenza è chiamata usualmente invariante
del ciclo per ovvi motivi.
Esercizio 71. Dimostrare la correttezza della regola di inferenza per l’iterazione.
2.6. Conseguenza. Avevamo già accennato che alcune asserzioni ovvie non possono
esser ottenute dalle regole che stiamo costruendo: a volte la premessa è “più debole”, altre
volte la conseguenza è “più forte”.
Per risolvere la questione è sufficiente estendere il sistema deduttivo con un paio di
regole che consentono di indebolire le premesse o rafforzare le conseguenze:
A → A0
B0 → B
{A0 } C {B}
{A} C {B}
{A} C {B 0 }
{A} C {B}
di cui, al solito, lasciamo la dimostrazione di validità per esercizio.
2.7. La logica di Hoare. La collezione di regole che abbiamo descritto (vedi Tabella 2) (integrato con la logica dei predicati) costituisce un sistema sistema logico-deduttivo
per le dimostrazioni di correttezza parziale del linguaggio di programmazione while.
La questione che non abbiamo ancora chiarito è se il sistema sia sufficientemente
espressivo. Tecnicamente questa questione si riduce a dimostrare due risultati:
correttezza:
` {A} I {B}
⇒
|= {A} I {B}
|= {A} I {B}
⇒
` {A} I {B}
validità:
Il primo risultato è essenzialmente ottenuto per induzione sulla struttura dell’algoritmo usando le dimostrazioni di correttezza delle regole; il secondo invece richiede una
dimostrazione più articolata che si può trovare in [Win99].
52
5. LA LOGICA DI HOARE
2.8. E la terminazione? In tutti i discorsi fatti in questo capitolo è rimasta in
sospeso la questione relativa alla terminazione. Per dimostrare la correttezza totale di un
algoritmo è necessario sapere che termina sempre.
Per raggiungere lo scopo si potrebbe estendere il sistema logico-deduttivo in modo da
includere la questione della correttezza; il sistema che si ottiene tuttavia fa perdere la
caratteristica di semplicità.
Oppure si può trattare la questione separatamente cercando con metodi puramente
matematici di scoprire se un algoritmo (o anche semplicemente un’iterazione) termina.
Se consideriamo l’iterazione while E do I end possiamo cercare di associare a tale
comando una funzione (detta funzione di terminazione)
τ : S −→ N
tale che
• σ 0 = φI (σ) allora τ (σ 0 ) < τ (0 σ);
• esiste k ∈ N tale che per ogni σ ∈ S,
τ (σ) ≤ k
⇒
ησ (E) = false
È facile dimostrare che se un iterazione ammette una funzione di terminazione allora
termina.
Esercizio 72. Trovare una funzione di terminazione per l’algoritmo della divisione
intera.
Bibliografia
[81]
Enciclopedia Garzanti di Filosofia. Garzanti, 1981. a cura di L. Boni.
[88]
Dizionario Italiano Ragionato. Sintesi, Firenze, 1988. a cura di G. D’Anna.
[AHU74] Alfred V. Aho, John E. Hopcroft, and Jeffrey D. Ullman. The Design and Analysis of Computer
Algorithms. addison, 1974.
[CLR90] T.H. Cormen, C.E. Leiserson, and R.L. Rivest. Introduction to Algorithms. MIT press, 1990.
[CLR94] T.H. Cormen, C.E. Leiserson, and R.L. Rivest. Introduzione agli Algoritmi. Jackson Libri, 1994.
Traduzione italiana di [CLR90].
[G8̈6]
Kurt Gödel. Collected Works. Oxford University Press, 1986.
[Gen69] Gerhard Gentzen. The collected Papers of Gerhard Gentzen. North-Holland, Amsterdam, 1969.
edited by M. E. Szabo.
[HU79] J. E. Hopcroft and J. D. Ullman. Introduction to Automata Theory, Languages and Computations.
Addison-Wesley, 1979.
[Man78] Zohar Manna. Teoria Matematica della Computazione. Boringhieri, 1978.
[Rog87] Hartley Rogers. Theory of recursive functions and effective computability. MIT Press, 1987.
[Win93] Glynn Winskel. The Formal Semantics of Programming Languages. An Introduction. MIT Press,
1993.
[Win99] Glynn Winskel. La semantica formale dei linguaggi di programmazione. UTET Libreria, 1999.
Traduzione italiana di [Win93].
[Wir77] Niklaus Wirth. Principi di Programmazione Strutturata. ISEDI, 1977.
53