Reasoning, Proof, and Formalization with Lean 4

Lezione 6 - Tipi induttivi, ricorsione e induzione

Stefano M. Nicoletti

Struttura della lezione

Promemoria: che cos'è l'induzione?

L'induzione è un modo per dimostrare che una proprietà vale per tutti gli elementi di una struttura costruita passo dopo passo.

Una dimostrazione per induzione è formata da:

L'ipotesi usata nel passo si chiama ipotesi induttiva, o ipotesi di induzione. Caso base e passo induttivo permettono insieme di raggiungere tutti gli elementi della struttura.

Da Cosa a un tipo con una struttura

Nelle lezioni precedenti, dichiarare Cosa : Type lasciava aperti gli elementi e la struttura del dominio.

Un tipo induttivo viene invece definito elencando tutti i modi in cui possiamo costruirne i valori. Questi modi si chiamano costruttori.

Per un semaforo consideriamo esattamente tre possibilità: rosso, giallo e verde.

Il tipo Semaforo

La dichiarazione di Semaforo introduce il tipo e i tre costruttori rosso, giallo e verde.

Ogni costruttore produce un valore del nuovo tipo. Poiché non abbiamo dichiarato altri costruttori, questi tre casi descrivono tutti i valori possibili.

Pattern matching e predicati

Un pattern matching definisce il risultato separatamente per ciascun costruttore.

Ritroviamo così un concetto già noto: SiDeveFermare : Semaforo → Prop è un predicato, ma ora conosciamo tutti gli elementi possibili del suo dominio.

Esercizio: ragionare sui costruttori

Vogliamo dimostrare che ogni semaforo è rosso, giallo oppure verde:

lecture06_semaforo_ha_un_colore (s : Semaforo) :

s = .rosso ∨ s = .giallo ∨ s = .verde.

Costruiamo i naturali

Definiamo un nuovo tipo Naturale senza usare Nat.

Per ora non usiamo numerali come 1 o 2: costruiamo i valori direttamente con zero e succ.

Zero, uno, due, tre

Dichiariamo:

Ogni valore finito è quindi una catena di applicazioni di succ che termina con zero.

Promemoria: che cos'è la ricorsione?

La ricorsione permette di risolvere un problema usando la soluzione dello stesso problema su un caso più semplice.

Una definizione ricorsiva contiene normalmente:

Le riduzioni devono infine raggiungere un caso base: altrimenti il calcolo continuerebbe senza terminare.

Che cos'è una definizione ricorsiva?

Una funzione ricorsiva segue i costruttori del dato sul quale opera.

Lean accetta la definizione perché, nel caso succ n, la funzione richiama se stessa su n: abbiamo tolto un succ e siamo quindi più vicini a zero. In questo modo il calcolo non può continuare a richiamare la funzione per sempre.

Definiamo la somma

Scriviamo somma (m n : Naturale) : Naturale: la funzione riceve due naturali e restituisce un naturale. Con match n with rendiamo esplicito che la ricorsione segue il secondo argomento.

Il secondo caso riduce il problema dalla somma con succ n alla somma con il naturale più piccolo n.

Definiamo il doppio

Scriviamo doppio (n : Naturale) : Naturale: la funzione riceve un naturale e restituisce un naturale. Con match n with analizziamo i costruttori di n.

Verifichiamo computazioni concrete come somma uno uno = due e doppio uno = due. In entrambi i casi Lean calcola i due lati e rfl chiude il goal.

Uguaglianza definizionale

Due espressioni sono definizionalmente uguali quando Lean può calcolarle fino a ottenere la stessa espressione.

In questi casi rfl chiude il goal perché, dopo la computazione, entrambi i lati coincidono.

Rendere visibile una definizione con unfold

unfold nome apre la definizione indicata nel goal.

Dimostriamo doppio uno = due: apriamo doppio, uno e due, quindi rfl riconosce che i due lati calcolano allo stesso valore.

Riscrivere con un'uguaglianza

Se abbiamo un'assunzione h : a = b, possiamo sostituire un lato con l'altro.

Riscrittura in avanti

Vogliamo dimostrare somma a zero = b, data l'assunzione h : a = b.

Prima di rw [h] il goal contiene a. Dopo la riscrittura contiene b. Il lato sinistro si riduce quindi a b e rfl chiude il goal.

Riscrittura nella direzione opposta

Vogliamo dimostrare somma b zero = a, data ancora l'assunzione h : a = b.

La freccia ← cambia l'orientamento della sostituzione. Leggiamo sempre il goal per decidere quale direzione avvicina l'espressione alla forma desiderata.

Un'uguaglianza che non si risolve per computazione

Confrontiamo:

Non basta controllare qualche valore di n: l'enunciato deve valere per tutti i naturali. Ci serve quindi un metodo che permetta di ragionare sistematicamente su ogni possibile costruzione di n.

Questo metodo è l'induzione. Vediamo ora come è fatta una dimostrazione per induzione.

Che cos'è una dimostrazione per induzione?

Per dimostrare una proprietà di ogni naturale:

Questo basta perché ogni Naturale è costruito a partire da zero mediante un numero finito di applicazioni di succ.

cases e induction

Entrambe le tattiche seguono i costruttori, ma forniscono informazioni diverse.

Se il passo per succ n deve riutilizzare il risultato per n, la sola analisi per casi non basta.

Come scegliere la variabile d'induzione

Non scegliamo automaticamente la prima variabile del theorem. Cerchiamo invece la definizione ricorsiva che non riesce a calcolare.

Nei teoremi più complessi, talvolta conviene prima riformulare o generalizzare il goal.

Prima induzione: zero a sinistra

Vogliamo dimostrare l'enunciato:

lecture06_zero_somma (n : Naturale) : somma zero n = n.

Ricordiamo la definizione che unfold somma rende visibile:

def somma (m n : Naturale) : Naturale :=

match n with

| zero => m

| succ n => succ (somma m n)

somma analizza il proprio secondo argomento. Nel goal quel posto è occupato da n, quindi facciamo induzione su n.

Leggere induction n with

Nel proof script:

Dove siamo arrivati

Abbiamo definito un tipo induttivo e funzioni ricorsive sui nostri naturali. Abbiamo poi usato rfl, unfold, rw e una prima dimostrazione per induzione.

Il risultato conclusivo è:

lecture06_zero_somma (n : Naturale) : somma zero n = n.

Nella prossima lezione useremo la stessa struttura per dimostrare altre proprietà della somma e introdurre simp.

Fonti

Theorem Proving in Lean 4, Inductive Types

Theorem Proving in Lean 4, Induction and Recursion

Theorem Proving in Lean 4, Tactics

Functional Programming in Lean, Datatypes and Patterns

Lean Language Reference, Recursive Definitions

Natural Number Game