Corso di Programmazione a.a. 2007-08 Lezioni di Claudio Mirolo e Fabio Alessi I Periodo Didattico =================== *** Lezione 1 - 27/09/07 (Claudio, Fabio) *** 0. Informazioni generali sul corso (pagine del corso). 1. Presentazione del corso e motivazioni delle scelte didattiche: - Obiettivi metodologici; - Discussione di esempi; - Ruolo dell'astrazione; - Strumenti di programmazione. 2. Ambiente di programmazione basato sul linguaggio Scheme. 2.1. Vantaggi: - semplicita' sintattica e d'uso, concisione; - attenzione focalizzata sul problema; - astrazione procedurale fin dall'inizio (funzioni, argomenti e risultati); - approccio ricorsivo; - superamento di stereotipi sulla programmazione; - sperimentazione didattica da parte di un'ampia comunita' internazionale (www.schemers.com). 2.2. Svantaggi: - assenza di tipi; - prestazioni non elevate; - scopi ed uso principalmente accademici. 3. Struttura del corso. Parte I - Astrazione procedurale; Parte II - Astrazione sui dati; Parte III - Astrazione relativa allo stato. Riferimenti: vedi anche la presentazione del corso. *** Lezione 2 - 28/09/07 (Fabio) *** 0. Digressione terminologica - Definizioni tratte dal vocabolario di termini impiegati in matematica e informatica: variabile, indeterminata, parametro, incognita, equazione, identita', uguaglianza, problema, definizione, soluzione. Esempio: Nozione di parametro confrontata con quella di variabile (dizionario Zingarelli). Parametro: Grandezza che compare in una espressione matematica o in una funzione, ed il cui variare influenza altre variabili presenti o la natura della funzione o dell'ente matematico descritto dalla espressione. Variabile: Ente non determinato, ma in grado di identificarsi con ciascuno degli enti di un determinato insieme. Interpretazione (provvisoria) e discussione. 1. Meccanismo di sostituzione formale di un argomento al posto di una variabile nell'applicazione di una funzione: lambda espressioni. 2. Parametri e corpo di una procedura in Scheme. 3. Formalizzazione e valutazione delle espressioni in Scheme. Struttura: ( ... ) *** Lezione 3 - 4/10/07 (Claudio) *** Espressioni in Scheme e relativa valutazione: 1. Esempi di espressioni aritmetiche e modalita' di valutazione: - Espressioni come entita' base; - Rappresentazione grafica di un'espressione; - Calcolo di un'espressione; - Procedure come astrazione di espressioni. 2. Formalizzazione delle espressioni in Scheme: - Aspetti sintattici: forma prefissa; - Modello di calcolo; - Esempi. 3. Esercizio: - espressione in Scheme per calcolare la superficie totale di un cilindro, dati i valori di raggio e altezza. 4. Astrazione procedurale: - Variabili all'interno di espressioni; - Astrazione procedurale (lambda-espressione); - Definizione di nomi; - Tipi degli argomenti e tipo del valore come commento (Scheme non e' un linguaggio tipato). 5. Applicazione di una procedura: - Modello di calcolo per sostituzione e riduzione; - Sostituzione dei valori degli argomenti; - Esempi: conversione Euro-Lire, area di un trapezio. 6. Esempi non numerici. - Espressioni per l'elaborazione di stringhe: Calcolo del participio passato, data la forma infinita (coniugazioni regolari in -are/ire). - Espressioni per l'elaborazione di oggetti grafici: Segmenti rettiliei e relativa sovrapposizione (cenni e visualizzazione). 7. Esercizi: - definizione di una procedura per convertire i prezzi da Lire a Euro; - definizione di una procedura per calcolare la superficie totale di un cilindro in funzione di raggio e altezza; - procedura per determinare il diminutivo di un sostantivo; - applicazione del modello di calcolo al caso della superficie totale di un cilindro. Riferimenti: vedi anche cap. 1 del libro consigliato. *** Lezione 4 - 5/10/07 (Claudio) *** 1. Calcolo del participio passato, data la forma infinita: estensione a tutte le coniugazioni regolari. - Coniugazioni in -ere. - Necessita' di riconoscere e distinguere casi che danno luogo a valutazioni diverse - Strumenti: operazioni su stringhe e caratteri, verifica di condizioni. 2. Tipo stringa in Scheme. - Notazione per le stringhe; - Procedure predefinite: string-append, string-length, substring; - Esempi di procedure su stringhe. 3. Tipo carattere in Scheme. - Notazione per i caratteri; - Procedure predefinite: string-ref, char=?. 4. Valori booleani ed espressioni a valore booleano. - Insieme dei valori booleani: { vero, falso }, - Notazione Scheme: #t, #f. 5. Espressioni condizionali. - Espressione "if"; - Condizione: espressioni a valori booleani; 6. Confronto fra espressione "if" (paradigma funzionale) e comando "if" (paradigma imperativo). 6.1. Espressioni "if" (B espressione booleana; E1, E2 espressioni dello stesso tipo): (if B E1 E2) [Scheme] (B ? E1 : E2) [Java, C, C++] - Il valore dell'espressione "if" e' il valore di E1 oppure il valore di E2; - Devono essere presenti sia E1 sia E2, altrimenti il valore dell'espressione "if" puo' risultare indefinito. 6.2. Comandi "if" (B espressione booleana; S1, S2 comandi): if B then S1 else S2 [Pascal] if ( B ) S1; else S2; [Java, C, C++] - Sara' eseguito il comando S1 oppure il comando S2; - S2 puo' non essere presente, nel qual caso se la condizione espressa da B e' falsa semplicemente non si esegue S1. 7. Esercizio: - estensione dei casi a cui si applica la procedura per determinare il diminutivo di un sostantivo. Riferimenti: vedi anche cap. 1 del libro consigliato. *** Lezione 5 - 11/10/07 (Claudio) *** 1. Problema: programma in Scheme per determinare l'imposta lorda sulla base del reddito imponibile (Calcolo dell'IRPEF in base alle istruzioni per la compilazione del modulo "Unico 2007", quadro RN, p. 52). 1.1. Soluzioni proposte dagli studenti, basate sull'annidamento dei costrutti "if": (if ___ ___ (if ___ ___ (if ___ ___ ___))) 1.2. Costrutto "cond" per le espressioni condizionali: - Regole di valutazione e confronto con il costrutto "if". 1.3. Discussione: - Definizione di nomi simbolici e ruolo dei nomi; - Leggibilita' e manutenibilita' del codice. 1.4. Organizzazione del codice: - Condivisione del codice ricorrente; - Parametri dipendenti e parametri indipendenti; - Separazione della logica procedurale (di calcolo) dai dati tecnici (definiti dal Ministero: scaglioni, aliquote). 1.5. Raffinamenti: - Astrazione procedurale relativa all'espressione per il calcolo dell'imposta sulla base dei parametri dello scaglione; - Definizione procedurale dei valori dell'imposta corrispondenti alle soglie fra scaglioni di reddito. In questa prima fase, lo scopo dell'esempio e' riflettere sull'organizzazione del codice e sull'astrazione procedurale. L'astrazione procedurale comporta la separazione della logica del calcolo, stabile nel tempo, dai parametri "di configurazione", soggetti ad aggiornamenti o personalizzazioni. 2. Esempi di procedure ricorsive: Calcolo delle lunghezze dei lati di un foglio in formato "Ak". - Il formato "Ak" e' un rettangolo (geometricamente) simile a quello che si ottiene piegandolo a meta' in modo da sovrapporre i lati piu' corti. Questa proprieta' determina il rapporto fra le lunghezze dei due lati. - Convenzionalmente, il formato A0 e' un rettangolo definito come sopra e avente una superficie di 1 mq. Da questa definizione conseguono le misure dei lati del formato A0, e quindi A1, A2, A3, A4, ecc. 3. Esercizi: - aggiornamento dell'esempio disponibile attraverso le pagine del corso, riferito al modello "Unico2007", con i dati relativi a dichiarazioni dei redditi degli anni precdenti (vedi appunti associati all'esempio); - procedura per trasformare valori naturali nell'intervallo [0, 15] in cifre esadecimali, rappresentate come stringhe; - annidamento dei costrutti di scelta "if" ai fini della riduzione del numero di confronti nel caso peggiore, con riferimento al calcolo dell'irpef e alla soluzione dell'esercizio precedente; - procedura per verificare la parita' di una sequenza binaria (parity check). Riferimenti: vedi anche cap. 2 del libro consigliato. *** Lezione 6 - 12/10/07 (Fabio) *** 0. Definizioni ricorsive e definizioni induttive. - In matematica la ricorsione riguarda la struttura formale che esprime le proprieta' a cui un'incognita deve soddisfare (p. es: equazione); - In matematica una definizione induttiva (in genere ricorsiva) suggerisce come una soluzione puo' essere costruita; - In informatica una definizione ricorsiva presuppone anche un modello di calcolo della soluzione; - Esempi di definizioni ricorsive che non sono ben fondate (e alle quali l'attributo induttivo non si applica). 1. Tipi numerici in Scheme. - I numeri interi sono (sostanzialmente) illimitati; - I numeri razionali hanno una rappresentazione specifica, con precisione illimitata, espressa in forma frazionaria; - I numeri reali (irrazionali) sono rappresentati in forma approssimata, floating-point, come avviene per la maggior parte dei linguaggi (Pascal, C, Java, ...); - Applicazione di operazioni a numeri rappresentati in forma esatta, approssimata o mista; - I numeri complessi hanno una rappresentazione e una notazione specifica. 2. Ricorsione. - Esempi di procedure ricorsive: fattoriale, parity-check. - Riduzione della complessita' nell'invocazione ricorsiva: "distanza" dai casi base. - Modello computazionale per sostituzione e riduzione e valutazione di procedure ricorsive. - Procedure ricorsive mal fondate: la "distanza" dai casi base non si riduce per l'invocazione ricorsiva. 3. Esercizi (vedi anche esempi nelle pagine del corso): - sperimentazione di procedure con valori interi "molto grandi"; - sperimentazione di procedure con argomenti e valori razionali; - procedura "first-a?" per verificare se il primo carattere di una stringa non vuota e' #\a; - procedura "detect-a?" per verificare se il carattere #\a occorre in una stringa; - procedura "how-many-a" per conoscere quante volte il carattere #\a occorre in una stringa. Riferimenti: vedi anche cap. 2 del libro consigliato. *** Lezione 7 - 18/10/07 (Claudio) *** 0. Richiami preliminari: - In Scheme non e' necessario gestire l'input/output; - Utente e programmatore si esprimono nello stesso linguaggio nell'ambito del modello funzionale; - Scheme non e' tipato, ma e' opportuno documentare i tipi degli argomenti e dei valori delle procedure tramite commenti. 1. Programma per determinare l'imposta lorda: revisione. 1.1. E se cambia il numero di scaglioni? - Individuazione delle regolarita' (taglia e incolla...); - Ulteriore astrazione della logica procedurale. 1.2. Discussione. - Regolarita' degli indici (k) dei parametri che identificano uno scaglione: (imposta imponibile inf-scaglione-k base-scaglione-k aliquota-k) - Sintesi piu' astratta, per r nel k-imo scaglione: imposta(r,k) = base(k) + aliq(k) * (r - inf(k)) - Osservazione sulla definizione di base(k): se k = 1 allora base(k) = 0; se k > 1 allora base(k) = imposta(inf(k),k-1). 2. Interpretazione ricorsiva. 2.1. Logica del calcolo dell'imposta (r = reddito imponibile): imposta(r,k) = aliq(k) * r se k=1; imposta(r,k) = imposta(inf(k),k-1) + aliq(k) * (r - inf(k)) se k>1. 2.2. Concisione nell'esprimere la logica risolutiva. 2.3. Procedura ricorsiva per il calcolo dell'IRPEF. - Processo di calcolo: irpef(r) = imposta(r,scaglione(r)) - Ricorsione e informazioni necessarie al processo computazionale (aggiunta di parametri): indice(r,k) - Esplicitazione delle ipotesi relative ai parametri aggiuntivi: r non supera il k-imo scaglione. - Impostazione: se r >= inf(k) allora indice(r,k) = 0; altrimenti indice(r,k) = indice(r,k-1). - Per n scaglioni: scaglione(r) = indice(r,n). 2.4. Codifica delle procedure "indice" e "scaglione" 3. Accesso indicizzato alla piccola base i dati (simulazione di array). - Due sezioni: logica procedurale (stabile nel tempo) e base di dati (contingente); - Array come funzioni da un insieme di indici all'insieme dei valori delle componenti. 4. Esempi di procedure ricorsive per l'elaborazione di stringhe: - Procedura "how-many" per determinare il numero di occorrenze di una parola in un testo (senza sovrapposizioni); - Procedura "repalcement" per rimpiazzare le occorrenze di una una parola con un'altra in un testo: (replacement "the" "a" "the cat catches the mouse") -*-> "a cat catches a mouse" (esercizio). 5. Esercizi: - completare il codice e sperimentare il programma ricorsivo per il calcolo dell'IRPEF, confrontando i risultati con quelli della versione precedente; - definizione della procedura "replacement" del punto 4. Riferimenti: vedi anche sez. 2.1 del libro consigliato. *** Lezione 8 - 19/10/07 (Fabio) *** 1. Definizione della procedura "replacement" (esercizio della lezione 7): - Costrutto "let". 2. Valori booleani, predicati, test, espressioni booleane. 2.1. Insieme dei valori booleani: { vero, falso }, in Scheme: { #t, #f }. 2.2. Operatori logici (booleani). - Binari: and, or; - Unario: not; - Rispettive tabelle di verita'. 2.3. Predicato: procedura che, applicata agli argomenti che le competono, restituisce come risultato un valore booleano. 3. Principali predicati predefiniti su numeri, stringhe, caratteri. 3.1. Predicati su numeri. - Operatori di confronto numerico: =, <=, <, >, >= - Altri predicati: number? 3.2. Predicati su stringhe. - string? - string=? 3.3. Predicati su caratteri. - char? - char=? 4. Stringhe e caratteri in Scheme. 4.1. Stringa: sequenza di caratteri (Una stringa di 1 carattere non e' un carattere!). 4.2. Stringa vuota: "" (" " stringa di un unico carattere, lo spazio bianco, non e' una stringa vuota!). 4.3. Procedure principali: - string?, string=?; - string-length, string-ref; - substring, string-append. 4.4. Notazione Scheme per i caratteri. 4.5. Distinzione fra caratteri (es: #\a) e stringhe (es: "a"), fra caratteri (es: #\1) e numeri (es: 1), fra stringhe (es: "12") e numeri (es: 12). 5. Ricorsioni mal fondate. - Esempio: Perche' la ricorsione seguente e' mal definita? (define ill-founded (lambda () (ill-founded))) - Esempio solo apparentemente simile (ma non si tratta di ricorsione): (define what-am-I-doing? (lambda () what-am-I-doing?)) Qual e' il risultato della valutazione di (((who-am-I?))) ? Perche'? 6. Problemi di conversione fra rappresentazioni numeriche in basi diverse. 6.1. Rappresentazione binaria (base 2) e esadecimale (base 16), come stringa, di un numero naturale; 6.2. Rappresentazione della generica cifra esadecimale. 7. Esercizi: - procedura per costruire il vezzeggiativo di un sostantivo (nei casi in cui si inserisce "ett" fra gli ultimi due caratteri); - definire una procedura "infinito?" che risponde (restituisce la stringa) "forse e' un infinito" se la stringa argomento termina in "are" "ere" oppure "ire" (usando l'operatore or), altrimenti risponde "probabilmente non e' in infinito". - argomentazione informale relativa alla correttezza. Riferimenti: vedi anche cap. 3 del libro di testo. *** Lezione 9 - 25/10/07 (Claudio) *** 0. Verifica formale della correttezza dei programmi. 0.1 In ogni caso e' necessario ragionare sui programmi: Quali strumenti consentono di argomentare in modo piu' rigoroso? 0.2. Significato: - La correttezza riguarda la logica algoritmica; - E' un concetto relativo alle specifiche; - Presuppone un modello astratto di computazione. 0.2. Motivazioni pragmatiche, culturali e didattiche: - La formalizzazione delle specifiche (assunzioni, risultati attesi) contribuisce alla chiarificazione del problema e alla documentazione della soluzione proposta. - L'analisi formale e' importante nei casi in cui i programmi assolvono funzioni cruciali o sono diffusamente utilizzati. - L'uso di due linguaggi strutturalmente diversi, il linguaggio di programmazione e il linguaggio per esprimere le specifiche e le proprieta' dei valori calcolati, consente di affrontare il problema da due punti di vista, facendo emergere piu' facilmente eventuali errori logici. - Dal punto di vista dell'apprendimento, favorisce lo sviluppo di atteggiamenti mentali che portano a scrivere programmi piu' chiari e concisi. 1. Dimostrazioni di correttezza delle procedure ricorsive: Algoritmo per la somma dei primi n numeri pari. - Dimostrazione per induzione che ogni n naturale : (even-sum n) -*-> n(n+1) 2. Dimostrazioni di correttezza delle procedure ricorsive: Algoritmo "alla Russa" per l'evamento a potenza (induzione completa). 2.1. Correttezza e specifiche: - Correttezza relativa alle specifiche; - Assunzioni sui valori dei parametri; - Proprieta' del risultato (valore della procedura). ;; ogni x > 0 naturale, ogni y naturale ;; power(x,y) = x^y [x elevato alla y] (define power ; assunzioni sui parametri: (lambda (b e) ; b > 0, e : naturali (cond ((= e 0) 1) ((even? e) (power (* b b) (quotient e 2))) (else ; e dispari (* b (power b (- e 1)))) ) )) ; valore risultante: b^e 2.2. Impostazione di una dimostrazione per induzione. - Proprieta' che si vuole dimostrare e campo di validita': ogni y naturale, ogni x > 0 naturale : (power x y) -*-> x^y - Proprieta' che esprimono i casi base: ogni x > 0 naturale : (power x 0) -*-> x^0 = 1 - Formulazione dell'ipotesi induttiva, preso n > 0 naturale: ogni y < n naturale, ogni x > 0 naturale : (power x y) -*-> x^y - Proprieta' da dimostrare nel passo induttivo, per n > 0 relativo all'ipotesi induttiva: ogni x > 0 naturale : (power x n) -*-> x^n 2.3. Dimostrazione dei casi base: (power x 0) ---> (cond ((= 0 0) 1) ((even? 0) (power (* x x) (quotient 0 2))) (else (* x (power x (- 0 1)))) ) ---> (cond (#t 1) ((even? 0) ...) (else ...)) ---> 1 2.3. Dimostrazione del passo induttivo: (power x n) ---> (cond ((= n 0) 1) ((even? n) (power (* x x) (quotient n 2))) (else (* x (power x (- n 1)))) ) ---> (cond (#f 1) ((even? n) ...) (else ...)) ; n > 0 a) n pari: ---> (cond (#f 1) (#t (power (* x x) (quotient n 2))) (else (* x (power x (- n 1)))) ) ---> (power (* x x) (quotient n 2)) -*-> (x^2)^(n/2) ; ipotesi induttiva poiche' n/2 < n = x^n b) n dispari: ---> (cond (#f 1) (#f (power (* x x) (quotient n 2))) (else (* x (power x (- n 1)))) ) ---> (* x (power x (- n 1))) -*-> (* x x^(n-1))) ; ipotesi induttiva poiche' n-1 < n ---> x * x^(n-1) = x^n 3. Esercizi (vedi esempi "Ricorsione e induzione" nelle pagine del corso): - espressioni che rappresentano i valori delle procedure "odd" e "unknown"; - relative dimostrazioni di correttezza; - sequenza di valori assunti da una procedura "ufo" (qual'e' la logica algoritmica?). Riferimenti: vedi anche cap. 2 del libro di testo, in particolare sezione 2.2. *** Lezione 10 - 26/10/07 (Fabio) *** 1. Uso del costrutto "let" per migliorare l'efficienza di un programma. - Esempio: (define prova (lambda (n) (if (= n 0) 1 (+ (prova (- n 1)) (prova (- n 1)))) )) e variante piu' efficiente (perche'?): (define prova (lambda (n) (if (= n 0) 1 (let ((m (prova (- n 1)))) (+ m m))) )) - Dimostrazione di correttezza: (prova x) -*-> 2^x per ogni x naturale 2. Esempio di procedure ricorsive: Frazioni continue in relazione alle equazioni di II e III grado. - Metodi iterativi per calcolare soluzioni di equazioni; - Programma in Scheme per approssimare il limite della frazione continua; - Soluzioni approssimate di equazioni di secondo grado; - Soluzioni delle equazioni di terzo grado, con e senza "let"; - Considerazioni relative all'efficienza nell'uso di numeri esatti nell'esempio: (define soluz-3-gr (lambda (b c d n) (if (= n 0) #i1 ; rappresentazione inesatta (floating-point) di 1 ; perche' l'utilizzo di 1 (esatto) e' meno efficiente? (let ((p (soluz-3-gr b c d (- n 1)))) (+ b (/ c p) (/ d (* p p)))) ))) 3. Problemi di conversione fra rappresentazioni numeriche in basi diverse. 3.1. Interpretazione di un numerale rappresentato in base due o sedici: - Procedura focalizzata sulla cifra meno significativa (scansione del numerale da destra a sinistra); - Procedura focalizzata sulla cifra piu' significativa (scansione del numerale da sinistra a destra); - Introduzione di parametri di supporto e ricorsione di coda; - Cenni al concetto di invariante per argomentare la corettezza. 3.2. Composizione delle procedure per la rappresentazione binaria/esadecimale e per l'interpretazione numerica: - Verifica che l'interpretazione a valori interi della rappresentazione binaria/esadecimale di un numero naturale restituisce il numero stesso. 4. Esercizi: - converisoni di rappresentazione fra basi generiche (2<= b1, b2 <= 16), dove i numerali (argomento e valore) sono rappresentati come stringhe. Riferimenti: vedi anche cap. 3 del libro di testo. *** Lezione 11 - 8/11/07 (Claudio) *** 1. Discussione delle soluzioni degli esercizi della lezione 9. 1.1. Proprieta' da dimostrare per "odd": ogni n > 0 naturale : (odd n) -*-> 2n-1 1.2. Proprieta' da dimostrare per "unknown": ogni n naturale : (unknown n) -*-> n^2 - caso base: (unknown 0) -*-> 0 (dimostrazione immediata) - ipotesi induttiva, fissato n > 0 : (unknown n-1) -*-> (n-1)^2 - passo induttivo (n > 0 fissato sopra): (unknown n) ---> (if (= n 0) 0 (+ (unknown (- n 1)) (odd n))) ---> (if #f 0 (+ (unknown (- n 1)) (odd n))) ; n > 0 ---> (+ (unknown (- n 1)) (odd n)) ---> (+ (n-1)^2 (odd n)) ; per l'ipotesi induttiva ---> (+ (n-1)^2 2n-1) ; proprieta' di "odd" ---> (n-1)^2 + 2n-1 = n^2 1.3. Proprieta' dei valori assunti dalla procedura "ufo" (discussione). - Nelle formule che seguono, si intende che le variabili n, j, k si riferiscono sempre a numeri naturali; - Definizione: (ufo n) -*-> f(n) (a) f(n) <= n ogni n > 0 : esiste j in [1,n] : (ufo n) -*-> j (b) f(n) dispari ogni n > 0 : esiste j >= 0 : (ufo n) -*-> 2j + 1 (c) f(2^k) = 1 ogni k >= 0 : (ufo 2^k) -*-> 1 (d) f(2^k-1) = 2^k-1 ogni k > 0 : (ufo 2^k-1) -*-> 2^k-1 (e) in generale ogni k >= 0 : ogni j in [0,2^k-1] : (ufo 2^k+j) -*-> 2j+1 - Impostazione delle dimostrazioni. 1.4. Proprieta' da dimostrare per "ufo": ogni k >= 0, ogni j in [0,2^k-1] : (ufo 2^k+j) -*-> 2j+1 (k, j naturali) - caso base: ogni j in [0,2^0-1] : (ufo 2^0+j) -*-> 2j+1 (ufo 2^0+0) -*-> (cond ((= 1 1) 1) ... ) -*-> 1 = 2j+1 perche' j = 0 - ipotesi induttiva, fissato n > 0 ogni j in [0,2^(n-1)-1] : (ufo 2^(n-1)+j) -*-> 2j+1 - passo induttivo, per n > 0 fissato sopra e per ogni j in [0,2^n-1]: (ufo 2^n+j) -*-> (cond ((= 2^n+j 1) 1) ((even? 2^n+j) ... ) (else ... )) -*-> ? - sottocaso (a): 2^n+j pari, da cui consegue j pari (anche 2^n e' pari per n > 0) e quindi j <= 2^n-2 -*-> (- (* 2 (ufo (quotient 2^n+j 2))) 1) ---> (- (* 2 (ufo 2^(n-1)+(j/2))) 1) poiche' n > 0 e j pari -*-> (- (* 2 (2(j/2)+1)) 1) per l'ipotesi induttiva, infatti j/2 in [0,2^(n-1)-1] (j <= 2^n-2) ---> (- 2(j+1) 1) poiche' 2(j/2) = j (j pari) ---> 2(j+1) - 1 = 2j+1 - sottocaso (b): 2^n+j dispari da cui consegue j dispari (2^n e' pari) -*-> (+ (* 2 (ufo (quotient 2^n+j 2))) 1) ---> (+ (* 2 (ufo 2^(n-1)+((j-1)/2))) 1) poiche' n > 0 e j dispari -*-> (+ (* 2 (2((j-1)/2)+1)) 1) per l'ipotesi induttiva, infatti (j-1)/2 in [0,2^(n-1)-1] (j <= 2^n-1) ---> (+ 2((j-1)+1) 1) poiche' 2((j-1)/2) = j-1 (j-1 pari) ---> 2j+1 2. Esercizi: - dimostrazione delle altre proprieta', senza far uso del risultato generale trattato sopra (punto 1.4). - espressione che rappresenta il valore della procedure "mistery" (vedi esempi "Ricorsione e induzione" nelle pagine del corso); - relativa dimostrazioni di correttezza (quali le difficolta'?). 3. Calcolo del massimo comun divisore (MCD) di una coppia di numeri naturali positivi. - Proprieta' individuate da Euclide (IV sec. a.C.) e definizione ricorsiva dell'algoritmo di calcolo: MCD(x,x) = x MCD(x,y) = MCD(x,y-x) se x < y MCD(x,y) = MCD(x-y,y) se x > y - Definizione della corrispondente procedura Scheme: "gcd". 4. Caratterizzazione della ricorsione di coda. 4.1. Richiamo della procedura per il calcolo della potenza alla Russa (non ricorsiva di coda, lezione 9): "power". 4.2. Confronto dei processi computazionali relativi all'algoritmo di Euclide per il MCD e all'algoritmo alla Russa per la potenza. - (power 2 7): power( b= 2, e=7 ) --> b * 64 = 128 \/ /\ power( b= 2, e=6 ) --> 64 \/ /\ power( b= 4, e=3 ) --> b * 16 = 64 \/ /\ power( b= 4, e=2 ) --> 16 \/ /\ power( b=16, e=1 ) --> b * 1 = 16 \/ /\ power( b=16, e=0 ) --> 1 - (gcd 60 18): gcd( x=60, y=18 ) --> 6 \/ /\ gcd( x=42, y=18 ) --> 6 \/ /\ gcd( x=24, y=18 ) --> 6 \/ /\ gcd( x= 6, y=18 ) --> 6 \/ /\ gcd( x= 6, y=12 ) --> 6 \/ /\ gcd( x= 6, y= 6 ) --> 6 4.3. Cosa caratterizza il secondo processo computazionale (gcd) rispetto al primo (power)? Riferimenti: vedi anche cap. 2 del libro di testo. *** Lezione 12 - 9/11/07 (Fabio) *** 1. Ricorsione ad albero: Numeri di Fibonacci. - Problema della crescita di una popolazione di conigli sotto ipotesi semplificative; - Analisi del processo risolutivo e stima del numero di ricorsioni: il numero di invocazioni della procedura ricorsiva ne supera il valore (uguaglianza solo nei casi base); - Problemi di intrattabilita' (complessita' di calcolo): crescita esponenziale del numero di ricorsioni; - Generalizzazione al variare delle condizioni iniziali; - Ricorsione di coda per calcolare i numeri di Fibonacci in modo piu' efficiente: indice + coppia di numeri consecutivi: (define fib (lambda (n) (fib-tr n 1 1))) (define fib-tr ...) - Cenni sul rapporto fra successione di Fibonacci, sezione aurea, e frazione continua corrispondente. 2. Ricorsione ad albero: Combinazioni di k oggetti presi da un insieme con n elementi. - Formulazione del problema ed esemplificazione; - Casi base; - Riduzioni ricorsive; - Codifica in Scheme; - Prestazioni dell'algoritmo (inefficiente: perche'?); - Combinazioni e coefficienti binomiali. 3. Esercizi: - individuare la relazione fra la crescita di una popolazione di conigli (ipotesi di Fibonacci) e i numeri di Fibonacci; - calcolo del numero dei percorsi di Manhattan. Riferimenti: vedi anche cap. 4 del libro di testo. *** Lezione 13 - 15/11/07 (Claudio) *** 1. Analisi dei processi risolutivi basati sulla ricorsione di coda. 1.1. Concetto di invariante. - MCD(x,y) e' invariante rispetto alle trasformazioni dei parametri (riduzioni ricorsive) 1.2. Relazione con l'iterazione (approccio iterativo): int gcd( int x, int y ) { // codice Java, C, C++, C# while (x != y) { if (x < y) { y = y - x; // (gcd x (- y x)) } else { x = x - y; // (gcd (- x y) y) }} return x; // x } 1.3. Codifica in Pascal per ulteriori confronti: function gcd( x, y: integer ) : integer; begin while (x <> y) do if x < y then y := y - x { (gcd x (- y x)) } else x := x - y; { (gcd (- x y) y) } gcd := x { x } end; 1.4. Relazione fra programma ricorsivo di coda (in Scheme) e programma iterativo (per esempio in Pascal). 2. Ricorsione generale e ricorsione di coda a confronto. 2.1. La ricorsione di coda e' una ricorsione e puo' essere discussa come qualunque altra ricorsione. 2.2. Approfondimento del concetto di invariante. 2.3. Esempio: procedure "mistery" e "process". - Qual e' il valore risultante di "process" in funzione dei parametri i, x, y ? ogni k, u, v naturali : (process k u v) -*-> k^2 + k(u - 1) + v - E' richiesto lo sforzo di caratterizzare situazioni piu' generali di quelle per le quali e' stato pensato l'algoritmo (dimostrazione di correttezza per esercizio). - L'analisi attraverso il concetto di invariante si limita invece al processo computazionale che realizza l'idea risolutiva: (mistery n) -*-> (process n 1 0 ) . . . -*-> (process n-j 2j+1 j^2 ) -*-> (process n-j-1 2j+3 (j+1)^2 ) . . . -*-> (process 0 2n+1 n^2 ) -*-> n^2 - Proprieta' invarianti (asserzioni) relative a qualunque chiamata di (process k u v) a partire da (mistery n): ( 0 <= k <= n ) & ( u = 2(n-k) + 1 ) & ( v = (n-k)^2 ) - Dimostrazione che l'invariante si conserva: k - 1 >= 0 poiche' k > 0 se c'e' ricorsione u + 2 = 2(n - k) + 3 = 2(n - (k-1)) + 1 v + u = (n - k)^2 + 2(n-k) + 1 = (n - (k-1))^2 3. Discussione: - Invarianti rappresentati da asserzioni; - Differenze fra il concetto di variabile nei programmi funzionali e nei programmi imperativi: trasparenza referenziale. 4. Esercizio: - riflessione sulla discussione dell'esempio "mistery"; - programma imperativo (while) corrispondente a "mistery"; - versione imperativa (Java o Pascal), invariante e dimostrazione di correttezza relativamente al programma: (define pw (lambda (b e) (proc b e 1))) (define proc (lambda (x y z) (cond ((= y 0) z) ((even? y) (proc (* x x) (quotient y 2) z)) (else (proc x (- y 1) (* x z))) ))) Riferimenti: vedi anche cap. 3 del libro di testo. *** Lezione 14 - 16/11/07 (Fabio) *** 1. Ricorsione ad albero: Percorsi di Manhattan. - Formulazione del problema ed esemplificazione; - Casi base e riduzioni ricorsive; - Codifica in Scheme e prestazioni dell'algoritmo; - Interpretazione combinatoria: relazione fra numero dei percorsi di Manhattan e numero di combinazioni. 2. Dimostrazione per induzione di correttezza. 2.1. Proprieta' da dimostrare: ogni m, n naturali : (manhattan m n) -*-> (m+n)! / (m! * n!) 2.2. Misura della "difficolta' del problema": k = m+n. - Dimostrazione per induzione (semplice) su k. 2.3. Dimostrazione del caso base: k = 0 ==> m = n = 0. (manhattan 0 0) ---> 1 = (0+0)! / (0! * 0!) 2.4. Ipotesi induttiva, fissato k > 0: ogni m, n naturali tali che m+n < k : (manhattan m n) -*-> (m+n)! / (m! * n!) 2.5. Dimostrazione del passo induttivo, per m+n = k fissato sopra: (manhattan m n) ---> (if (or (= m 0) (= n 0)) 1 (+ (manhattan (- m 1) n) (manhattan m (- n 1))) ) a) m = 0: ---> 1 = (0+n)! / (0! * n!) b) n = 0: analogo. c) m, n > 0: ---> (+ (manhattan (- m 1) n) (manhattan m (- n 1))) ---> (+ (manhattan m-1 n) (manhattan m n-1)) -*-> (+ (m-1+n)!/((m-1)!*n!) (manhattan m n-1)) ; ipotesi induttiva: m-1+n = k-1 -*-> (+ (m-1+n)!/((m-1)!*n!) (m+n-1)!/(m!*(n-1)!)) ; ipotesi induttiva: m+n-1 = k-1 -*-> (m-1+n)!/((m-1)!*n!) + (m+n-1)!/(m!*(n-1)!)) = (m-1+n)!/((m-1)!*(n-1)!) (1/n + 1/m) = (m+n)! / (m! * n!) 3. Esercizi: - sperimentazione del programma ricorsivo per il calcolo del numero di percorsi di Manhattan e valutazioni intuitive di efficienza; - numero di tassellazioni di un rettangolo 2xN con tasselli 2x1 e sequenza di Fibonacci; - numero di allineamenti di oggetti rossi e blu senza adiacenze di oggetti rossi e sequenza di Fibonacci. 4. Procedure con argomenti o valori procedurali: esempi numerici. 4.1. Lambda-espressioni: esempi. 4.2. Polinomi come procedure a valori funzionali: procedura che dati tre parametri restituisce la funzione polinomiale di secondo grado associata. 4.3. Procedura che dato un numero p restituisce la funzione "incremento di p". 4.4. Esempi di valutazione di espressioni corrette e scorrette. 4.5. Procedura che data una funzione f sui naturali restituisce f(0). 4.6. Procedura che data una funzione f e un naturale n calcola f(0) + f(1) + f(2) + ... + f(n). 5. Esercizio (argomenti e valori procedurali): - procedura che data una funzione f ed un naturale n restituire l'iterata n-esima di f. Riferimenti: vedi anche cap. 4 (ricorsione ad albero) e cap. 5 (argomenti procedurali) del libro di testo. *** Lezione 15 - 22/11/07 (Claudio) *** 1. Parametri procedurali. 1.1. Pretesto: Codice di Giulio Cesare (vedi esempi). 1.2. Semplici regole di codifica dei simboli (lettere) modellate attraverso procedure. 1.3. Codifica e ruolo del parametro funzionale "encrypt": (... (lambda (message encrypt) ...)) e ... (encrypt (string-ref message 0)) ... 1.4. Applicazione per una regola prefissata. 2. Valori procedurali (codifica di Giulio Cesare). 2.1. Generalizzazione della procedura di codifica secondo lo schema di Giulio Cesare per qualsiasi rotazione delle lettere. 2.2. Procedura con valori funzionali: (lambda (rot) ; argomento della procedura (lambda (letter) ... ) ; espressione a valore funzionale ) 2.3. Regole di codifica e di decodifica. - Esempi: (caesar 7) e (caesar -7) 3. Procedure con argomenti e valori procedurali (funzioni di ordine superiore). 3.1. Funzione di decrittazione data la funzione di crittazione. 3.2. "Rottura del codice": funzone inversa. 4. Esercizi: - revisione della regola di trasformazione dei caratteri (codice di Cesare) per consentire l'uso di lettere minuscole; - revisione per restringere l'applicabilita' all'alfabeto Latino del tempo (A B C D E F G H I K L M N O P Q R S T V X Y Z). Riferimenti: vedi anche cap. 5 del libro di testo. *** Lezione 16 - 23/11/07 (Fabio) *** 1. Ricorsione ad albero: Numeri di Stirling di II specie. - Significato combinatorio: Problema delle partizioni proprie: numero di partizioni di un insieme di n elementi in k sottoinsiemi non vuoti (1 <= k <= n); - Esemplificazione: in quanti modi si possono disporre 6 pasticcini in 3 piattini indistinguibili?; - Casi base e riduzioni ricorsive: st(n,k) = 1 se k=1 oppure k=n st(n,k) = st(n-1,k-1) + k*st(n-1,k) se 1 < k < n - Codifica in Scheme. 2. Calcolo del fattoriale generalizzato con la ricorsione di coda. - Caso ricorsivo: fatt-gen( p, q, n ) = fatt-gen( p*q, q+1, n ) 3. Integrazione approssimata di una funzione f sull'intervallo di estremi a, b: 3.1. Approssimazione: numero n di suddivisioni ddell'intervallo [a, b]. 3.2. Interpretazione e confronto delle seguenti astrazioni procedurali: (define integrale_0 (lambda (f a b n) (if (>= a b) 0 (let ((p (/ (- b a) n))) (+ (* p (f a)) (integrale_0 f (+ p a) b (- n 1))))))) (define integrale_1 (lambda (f) (lambda (a b n) (if (>= a b) 0 (let ((p (/ (- b a) n))) (+ (* p (f a)) ((integrale_1 f) (+ p a) b (- n 1)))))))) (define integrale_2 (lambda (a b) (lambda (f n) (if (>= a b) 0 (let ((p (/ (- b a) n))) (+ (* p (f a)) ((integrale_2 (+ p a) b) f (- n 1)))))))) 4. Ricapitolazione: Procedure con argomenti e/o valori procedurali (funzioni di ordine superiore). - Argomenti e valori di tipo numerico, booleano, stringa, immagine,... - ... Argomenti e/o valori di tipo procedurale. - Caratteristica tipica dei linguaggi funzionali: anche le procedure sono oggetti di elaborazione. - Codifica e ruolo del parametro funzionale f: (lambda (f n) ...) e (f (- n 1)) - Esempio: composizione di funzioni (f, g : D --> D). - Esempio: funzione inversa (funzioni biiettive). - Esempio: iterata di ordine generico. 5. Esercizi: - Sperimentazione degli esempi discussi. Riferimenti: vedi anche capp. 4 e 5 del libro di testo. II Periodo Didattico ==================== *** Lezione 17 - 17/01/08 (Claudio) *** Discussione degli esercizi della prima prova di accertamento. *** Lezione 18 - 18/01/08 (Fabio) *** 1. Dati strutturati in Scheme: Coppie. - Definizione di coppia ( _ . _ ); - Operazioni "cons", "car" e "cdr" in relazione alle coppie. 2. Dati strutturati in Scheme: Liste. - Definizione formale di lista e differenze rispetto alla sequenza; - Lista vuota e sue rappresentazioni sintattiche: null, (), '(); - Costruzione di liste attraverso l'operatore "cons"; - Accesso agli elementi attraverso le composizioni di "car" e "cdr" (e abbreviazioni sintattiche delle composizioni); - Verifica se una lista e' vuota: "null?"; - Costruzione di liste attraverso l'operatore "quote": '; - Costruttore di liste "list" e notazione semplificata per le liste; - Confronto fra "list" e "cons"; esempio: (list 1 2 3 4) = (cons 1 (list 2 3 4)) = ... = (cons 1 (cons 2 (cons 3 (cons 4 (list))))) = (cons 1 (cons 2 (cons 3 (cons 4 ()))))) - Liste eterogenee e liste come elementi di liste. 3. Note a margine: - Rappresentazione esplicita di liste e "quote"; - Confronto fra "list" e "quote"; - Confronto di '(x1 x2 ... xk) e (list 'x1 'x2 ... 'xk). 4. Esempi di procedure che operano su liste: - Procedura "length" per calcolare il numero di elementi di una lista; - Ricerca di un elemento in una lista (scansione lineare); - Estrazione dell'elemento in una data posizione (prima, ultima, generica); - Procedura per determinare l'elemento che compare in una data posizione di una lista (prima, ultima, generica); - Inserimento di un elemento in una data posizione (all'inizio, in coda, in posizione generica); - Procedura "append" per la giustapposizione di due liste. 5. Esercizio: - procedura "reverse" per il rovesciamento di una lista. Riferimenti: vedi anche il capitolo 7 del libro di testo. *** Lezione 19 - 24/01/08 (Claudio) *** 1. Esempio di applicazione delle liste: Rappresentazione ed elaborazione di sequenze di nucleotidi. 1.1. Determinazione del nucleotide complementare: rappresentazione atomica dei nucleotidi (quote); 1.2. Simulazione della replicazione di una catena di nucleotidi. 2. Nota a margine: - Confronto fra le notazioni: x, 'x, #\x, "x". 3. Esempio: Occorrenza e posizione di una sottosequenza (gene) all'interno di una catena di nucleotidi. - Riconoscimento di un "prefisso"; - Determinazione della posizione nel caso di occorrenza; - Convenzione per rappresentare la mancata occorrenza. 5. Esempio: Sottosequenza comune piu' lunga (lcs). 5.1. Definizione del problema. - Confronto fra catene di nucleotidi per conoscere le informazioni comuni (pattern matching nell'ambito della biologia molecolare); A G A C T G A A C A T A C | | / \ \ | / / / G A T T T G A C T A C - Problemi correlati: comando "diff" delle shell Unix/Linux (confronto di versioni diverse di file testuali). 5.2. Impostazione della soluzione. (lcs () v) = (lcs u ()) = (); (lcs (cons a u) (cons a v)) = (cons a (lcs u v)); (lcs (cons a u) (cons b v)) = (longer (lcs u (cons b v)) (lcs (cons a u) v)) se a <> b. 5.3. Codifica e discussione. - Ricorsione ad albero (terzo caso); - Prestazioni limitate (perche'?). 6. Esercizi: - procedura che restituisce la piu' lunga di due liste passate come argomenti; - come si potrebbero determinare la lista di tutte le soluzioni? Riferimenti: vedi anche introduzione alla II parte e sezioni 7.1-3 del libro di testo; cap. 4 per la ricorsione ad albero. *** Lezione 20 - 25/01/08 (Fabio) *** 1. Esempio di applicazione di liste: problema del triangolo di Steinhaus. - Rappresentazione delle righe del triangolo attraverso liste; - Triangolo di Steinhaus come lista di liste; - Programma in Scheme per la verifica della proprieta' di bilanciamento di 0 e 1. 2. Esempio: Fusione ordinata di due liste ordinate (merge). Riferimenti: vedi anche cap. 7 del libro di testo per quanto riguarda le liste in Scheme. *** Lezione 21 - 31/01/08 (Claudio) *** 0. Seconda parte del corso: Astrazione sui dati. - Astrazione per i tipi predefiniti. - Esempi (gia') considerati: tipi numerici, stringhe, immagini. - Caratterizzazione del tipo: operazioni consentite (protocollo). - Separazione degli aspetti relativi all'implementazione e all'uso di un tipo di dato e ruolo delle operazioni; - Sviluppo degli esempi: (i) analisi e definizione del protocollo, (ii) applicazione del dato astratto, (iii) realizzazione/i. 1. Astrazione sui dati: problema di Giuseppe Flavio (pretesto). - Descrizione del gioco: "conta"; - Esempio con n = 6 commensali: A* A A F B F F --> --> E C E C* E* C D D A* A --> --> --> E C E* E* - Da identificatori simbolici a identificatori numerici: gflavio(6) = 5 (E, quinto commensale). - Operazioni consentite: osservazioni/domande sullo stato e regole del gioco. 2. Definizione del dato astratto "tavola rotonda": protocollo (il costruttore verra' introdotto alla fine; il protocollo e' specificato in collaborazione con gli studenti). 3. Soluzione del problema di Giuseppe Flavio: Trasformazioni relative alla "tavola rotonda". 4. Realizzazione del dato astratto "tavola rotonda": - Come rappresentare la tavola con i commensali? - Come rappresentare il commensale con la moka? - Scelta: lista; - Realizzazione basata sulla lista di commensali interpretata ciclicamente: il primo elemento della lista ha la moka. - Decisioni finali: numerazione dei commensali e costruttore. 5. Esercizio: - sequenza dei risultati al variare del numero di commensali n = 1, 2, 3, ... (cosa suggerisce nel caso in cui i commensali sono numerati?) Riferimenti: vedi anche introduzione alla II parte e sez. 6.1 del libro di testo. *** Lezione 22 - 1/02/08 (Fabio) *** 1. Esempi di elaborazione di liste: Ordinamento. - Algoritmo "Bubble Sort": discussione e codifica; - Algoritmo "Selection Sort": discussione e codifica. Riferimenti: vedi anche la sez. 7.5 del libro di testo (ricorsione ad albero e ordinamento). *** Lezione 23 - 7/02/08 (Claudio) *** 1. Analisi dei costi computazionali: - Stime basate sul numero di operazioni chiave (in un senso che verra' precisato nel corso di Algoritmi e Strutture Dati); - Rilevanza delle scelte implementative. 2. Stima dei costi computazionali. 2.1. Numero di "cons" per aggiornare la lista in funzione del numero iniziale di commensali n: C(n). 2.2. Analisi: - Quante volte viene invocata "append-item"? (Ogni invocazione un "cons".) - A partire da una lista di k elementi, si ricostruisce una lista di lunghezza k-1: k-1 "cons". - (josephus n) richiede n-1 passi della conta, durante i quali vengono ricostruite liste di n-1, n-2, ... 2, 1 elementi: C(n) = n-1 + n-2 + ... + 2 + 1 = n(n-1) / 2 2.3. Esempi: C(10) = 45; C(50) = 1225. 3. Realizzazioni alternative del dato astratto "tavola rotonda": versione basata su una coppia di liste. 3.1. Obiettivo: riduzione dei costi della ricostruzione di liste. - Principale causa di inefficienza: ricostruzione completa della lista ad ogni passo del gioco. 3.2. La lista della realizzazione precedente e' spezzata in due: - I commensali della prima lista sono seguiti da quelli della seconda in ordine rovesciato; - La seconda lista e' vuota se la prima ha meno di due elementi; - Nella maggior parte dei casi e' sufficiente spostare un elemento da una lista all'altra, senza ricostruire la struttura; - Saltuariamente (circa ad ogni dimezzamento del nuero di elementi) la struttura deve essere completamente ricostruita. 4. Stima dei costi computazionali della nuova realizzazione. 4.1. Numero di "cons" per aggiornare le due liste in funzione del numero iniziale di commensali n: C(n). 4.2. Analisi: - Ad ogni passo ci sono almeno 2 "cons" attribuibili a "move-item" e (josephus n) richiede n-1 passi: C'(n) < 2n - Ad ogni dimezzamento del numero di commensali viene invocata la procedura "reverse-items", che ricostruisce la prima lista, di lunghezza (circa): n/2, n/4, n/8, ... C"(n) = n/2 + n/4 + ... + n/(2^i) + ... = n (1/2 + 1/4 + ... + 1/(2^i) + ...) < n - Costi complessivi: C(n) = C'(n) + C"(n) < 3n 4.3. Esempi: C(10) < 30; C(50) = < 150. 5. Esercizio: - sperimentare le prestazioni delle due rappresentazioni per elevati valori del parametro n (qualche migliaio). *** Lezione 24 - 8/02/08 (Fabio) *** 1. Esempio di elaborazioni relative a liste: ordinamento. 1.1. Algoritmo "Insertion Sort": discussione e codifica; 1.2. Algoritmo "Merge Sort": discussione e codifica; 1.3. Algoritmo "Quick Sort": cenni. 2. Cenni all'analisi della complessita' computazionale degli algoritmi di ordinamento. - Complessita' quadratica (Insertion Sort); - Complessita' n log n (Merge Sort); - Complessita' ed equazioni con ricorrenze (Merge Sort). 3. Esempio di elaborazioni di liste: rappresentazione di alberi (introduzione). Riferimenti: vedi anche la sez. 7.5 del libro di testo (ricorsione ad albero e ordinamento). *** Lezione 25 - 14/02/08 (Fabio) *** 1. Rappresentazione di alberi generici tramite liste. 2. Rappresentazione e visita di alberi binari. - Visita in pre-ordine (anticipata); - Visita in post-ordine (posticipata); - Visita in in-ordine (simmetrica). 2. Alberi binari di ricerca (BST). - Proprieta' di un BST; - Ricerca di un elemento (intero) in un albero generico e in un BST. 3. Esercizio: - procedura per verificare se un albero binario e' un BST. *** Lezione 26 - 21/02/08 (Claudio) *** 1. Astrazione sui dati: Problema delle "n regine". - Presentazione del problema: dimensione numero di soluzioni n = 1 1 2 0 3 0 4 2 5 10 6 4 7 40 8 92 9 352 10 724 11 2680 12 14200 - Discussione del processo risolutivo come ricerca nello spazio degli stati (configurazioni della scacchiera); - Albero (n-ario) di ricerca: foglie e soluzioni. 2. Specifica del protocollo del dato astratto "scacchiera". 3. Calcolo del numero di soluzioni per una scacchiera n x n. - Ricerca nello spazio delle configurazioni; - Visita in profondita' dello spazio di ricerca: ruoli delle procedure "number-of-completions" e "sibling-boards-from". *** Lezione 27 - 22/02/08 (Claudio) *** 1. Realizzazione del dato astratto "scacchiera". - Informazioni rappresentate; - Ridondanza; - Assunzioni: disposizione progressiva riga dopo riga; - Lista come record (aggregato) di informazioni; - Componente procedurale: predicato per verificare se una posizione della scacchiera e' minacciata da altre regine; 2. Elaborazione dell'elemento procedurale (predicato). B : B': --------------- --------------- | | Q | | | | | Q | | | |---|---|---|---| |---|---|---|---| | | | | Q | (add-next-queen B j) | | | | Q | |---|---|---|---| -----------------------> |---|---|---|---| | | | | | i | Q | | | | |---|---|---|---| |---|---|---|---| | | | | | | | | | | --------------- --------------- j (car B) : --------------- |#t |#t |#t |#t | |---|---|---|---| if e' sotto scacco di |#t |#t |#t |#t | |---|---|---|---| then e' sotto scacco in B' |#f |#t |#t |#t | |---|---|---|---| else e' sotto scacco in B' |#f |#t |#f |#t | se lo e' in B --------------- - Prorpieta' relative agli indici (verificale). e' sotto scacco di se y = j : stessa colonna, oppure x-y = i-j : stessa diagonale \, oppure x+y = i+j : stessa diagonale /. 3. Determinazione della lista di soluzioni. - Rappresentazione della disposizione; - Adattamento della soluzione del problema precedente. 4. Esercizi: - modifica della realizzazione delle scacchiere in modo tale che la procedura restituisca l'immagine della disposizione delle regine (vedi esercizi proposti nella pagina degli esempi); - realizzazione alternativa basata su insiemi di colonne e diagonali libere. *** Lezione 28 - 28/02/08 (Claudio) *** 1. Codifica di Huffman per la compressione di documenti. 1.1. Compressione dell'informazione su una sequenza di simboli. Esempio (sequenza di nucleotidi, 12 simboli): A T T C T A C C T T G T 1.2. Codifica standard a parola fissa (2 bit per 4 simboli): A -> 00, T -> 01, C -> 10, G -> 11 - Codifica (24 bit): A T T C T A C C T T G T -> 000101100100101001011101 1.2. Codifica di Huffman: - Peso dei simboli = numero di occorrenze (o frequenza); coppie simbolo/peso ordinate per peso crescente: ( G:1 A:2 C:3 T:6 ) - Costruzione dell'albero di Huffman; aggregazione e riordinamento dei due elementi di minor peso: ( /\ : 1+2=3 C:3 T:6 ) G A ( /\ : 3+3=6 T:6 ) /\ C G A ( /\ . 6+6=12 ) /\ T /\ C G A - Codice = percorso nell'albero per raggiungere il simbolo (0=sinistra, 1=destra): A -> 001, T -> 1, C -> 01, G -> 000 - Codifica (21 bit): A T T C T A C C T T G T -> 001110110010101110001 - Decodifica: interpreto la sequenza binaria come istruzioni per scendere attraverso l'albero (0/sinistra, 1/destra); quando arrivo a una foglia, riporto il simbolo corrispondente e ritorno dalla radice dell'albero. 2. Astrazione sui dati: Specifica del protocollo del dato astratto "struttura di Huffman". 3. Realizzazione della struttura di Huffman. - Componenti: albero di huffman, peso e tabella delle codifiche; - Ridondanza per facilitare le operazioni: albero, peso (cumulativo), lista di simboli e corrispondente lista di codici; - Struttura iniziale: costruttore huffman-structure; - Aggregazione di strutture: huffman-merge; - Applicazione della procedura predefinita "map". 4. Esercizio: - definire le procedure huffman-weight (peso dei simboli di un albero) e huffman-code (codifica binaria di un simbolo). *** Lezione 29 - 29/02/08 (Fabio) *** 1. Rappresentazione di espressioni: - Espressioni postfisse: Reverse Polish Notation (RPN); - Espressioni prefisse: Espressioni Scheme; - Espressioni infisse. 2. Rappresentazione di espressioni ad albero. - Visite di alberi di espressioni; - Conversioni fra rappresentazioni. Riferimenti: vedi anche il cap. 8 del libro di testo, che affronta gli alberi di espressioni (sez. 8.3). *** Lezione 30 - 6/03/08 (Fabio) *** 0. Codifica di Huffman (seguito della lezione 28). 1. Codifica e decodifica: huffman-code, huffman-decode. 2. Protocollo e realizzazione della struttura "liste ordinate" (di strutture di Huffman). 3. Costruzione di una struttura di Huffman per dati pesi dei simboli. 4. Procedura per la compressione di una sequenza di simboli, data una struttura di Huffman. 5. Procedura per la decompressione (decodifica). III Periodo Didattico ===================== *** Lezione 31 - 24/04/08 (Claudio) 0. Obiettivi generali della terza parte del corso. 0.1. Punto della situazione: Astrazione procedurale e astrazione sui dati. 0.2. Concetto di stato. - Approccio imperativo e introduzione al linguaggio Java; - Perdita della "trasparenza referenziale". 0.3. Strumenti linguistici per esprimere l'astrazione sullo stato: concetto di oggetto. - Struttura/organizzazione della memoria e del codice; - Metodologia: Programmazione Orientata agli Oggetti. 1. Concetto di stato e paradigma imperativo. - Paradigma funzionale e paradigma imperativo a confronto; - Vantaggi del paradigma funzionale: trasparenza referenziale (la stessa variabile in punti diversi denota lo stesso valore); - Vantaggi del paradigma imperativo: economia di risorse. 2. Applicazione del paradigma imperativo al problema delle n regine: rappresentazione della scacchiera attraverso vettori. - Richiamo della versione funzionale; - Protocollo: acquisizione di informazioni e operazioni sulla scacchiera; - Applicazione dei vettori in Scheme: principali operazioni; - Struttura della rappresentazione: disposizione delle regine; - Struttura della rappresentazione: colonne e diagonali sotto scacco; - Realizzazione del protocollo secondo il paradigma imperativo; - Modifica dello stato: revisione dell'operazione "add-next-queen". 3. Esercizi: - completare la realizzazione: codifica della procedura arrangements; - integrazione con il programma funzionale per risolvere il problema delle N regine: perche' non funziona? Riferimenti: sul concetto di stato vedi anche il cap. 11 del libro di testo, e in particolare la sez. 11.6. *** Lezione 32 - 2/05/08 (Fabio) 1. Introduzione al linguaggio di programmazione Java: - Concetti di classe e oggetto; - Programma principale (main). 2. Esempi di classi in Java. - Numeri naturali: metodi succ() e getValue(); - Punti del piano e vettori bidimensionali. Riferimenti: classi, oggetti, variabili di istanza e metodi sono introdotti nel cap. 4. del libro di Lewis & Loftus. *** Lezione 33 - 8/05/08 (Claudio) 1. Problema delle N regine: Applicazione dello schema algoritmico funzionale. - Risultati: (queens-arrangements 1) --> 1 ... ... (queens-arrangements 4) --> 0 (queens-arrangements 5) --> 4 (queens-arrangements 6) --> 0 (queens-arrangements 7) --> 6 (queens-arrangements 8) --> 0 - Riflessione: Qual e' il problema? (*) ... ... (+ (queens-completions (add-next-queen board c)) (completions-from (+ c 1) board))) ... ... - Discussione: ripristino di stati precedenti; ... ... (+ (queens-completions (add-next-queen board c)) (completions-from (+ c 1) (remove-last-queen board)))) ... ... - Integrazione del protocollo: (remove-last-queen ). - Perche' con la versione scorretta (*) (queens-arrangements 5) conta ripetutamente la soluzione <1, 3, 5, 2, 4> ? 2. Versione imperativa: "number-of-arrangements", "list-of-arrangements". 3. Realizzazione della struttura dati "scacchiera" in Java: classe "Board" (impostazione introduttiva). 4. Esercizi: - completare la realizzazione di "remove-last-queen"; - sperimentare la versione corretta del programma in Scheme. Riferimenti: sul concetto di stato vedi anche il cap. 11 del libro di testo, e in particolare la sez. 11.6. *** Lezione 34 - 9/05/08 (Fabio) 1. Obiettivi degli argomenti affrontati in seguito. - Organizzazione della memoria: vettori (array) e matrici; - Tecniche per migliorare l'efficienza (economia di risorse): "memoization" e "dynamic programming"; - Introduzioni di alcuni elementi di Java come linguaggio di programmazione (imperativa) strutturata e procedurale; - Array: vettori e matrici in Java. 2. Esempio rivisitato: Numeri di Fibonacci. 2.1. Richiamo della soluzione ricorsiva in Scheme: - Motivi dell'inefficienza della soluzione ricorsiva; - Ricordare cosa e' gia' stato calcolato implica il concetto di stato. 2.2. Soluzione strutturalmente equivalente in Java: traduzione. - Alcuni aspetti sintattici di Java; - Comandi (if/else) ed espressioni (_?_:_) condizionali a confronto; - Rappresentazione degli interi in Java: "int" e "long" (intervalli di numeri rappresentabili). 2.3. Tecnica di memoization. - Parametro di stato: array "history" per rappresentare la storia computazionale; - Array in Java; - Reinterpretazione del nucleo ricorsivo; - Contestualizzazione basata sullo stato; - Definizione del metodo base. 3. Esercizio: - sperimentazione delle due versioni del programma (tempi di calcolo). Riferimenti: sulla tecnica di memoization vedi anche le prime sezioni del cap. 12 del libro di testo, in particolare la sez. 12.3. Per quanto riguarda il linguaggio Java, i metodi statici sono accennati nella sez. 5.2.2 del libro di Lewis & Loftus; costanti, variabili, assegnazioni e tipi predefiniti sono presentati nelle sezz. 2.3-2.4; i costrutti di controllo per scelta e iterazione e le principali espressioni sono introdotti nel cap. 3, in particolare sezz. 3.1-3.2, 3.4-3.6 e 3.8; gli array unidimensionali nelle sezz. 6.1-6.2 e la ricorsione in Java nel cap. 11, sezz. 11.1-11.3. *** Lezione 35 - 15/05/08 (Claudio) 1. Concetti di classe e oggetto in Java. - Strumenti linguistici per l'astrazione sullo stato (e sui dati); - Distinzione fra ruolo e responsabilita' dell'implementatore e dell'utilizzatore; - Una classe rappresenta uno schema, non un oggetto. - Una classe definisce un tipo (nome) e il relativo protocollo (circoscritto). 2. Trasposizione in Java del programma che risolve il problema delle n regine: struttura del protocollo della classe Board. 2.1. Costruttore: - b = new Board(n) 2.2. Metodi per acquisire informazioni sullo stato (funzionali): - b.boardSize() - b.queensOnBoard() - b.underAttack(j) - b.arrangement() 2.3. Metodi per modificare lo stato (non funzionali): - b.addNextQueen(j) - b.removeLastQueen() 3. Rappresentazione dello stato di una istanza della classe Board. 3.1. Variabili di istanza; 3.2. Attributo "private" e incapsulamento; 3.3. Realizzazione del costruttore relativo alla classe Board: - Inizializzazione delle variabili di istanza; - Parametri del costruttore. 4. Codifica dei metodi. 5. Codifica in Java dell'algoritmo risolutivo. - Programmi come collezioni di classi. - Revisione in stile imperativo dello schema algoritmico; - Metodi procedurali "void"; - public static void main( String[] args ) { ... }. 6. Esercizio: - completare la codifica e sperimentare il programma. Riferimenti: classi, oggetti, variabili di istanza e metodi sono introdotti nel cap. 4. del libro di Lewis & Loftus, in particolare vedi sezz. 4.1-4.5; al riferimento "this" si accenna nella sez. 5.1.5. *** Lezione 36 - 16/05/08 (Fabio) 1. Memoization: Rivisitazione della soluzione del problema dei Percorsi di Manhattan. 1.1. Richiamo delle soluzioni ricorsive in Scheme. 1.2. Traduzione in Java. - Alcuni aspetti sintattici di Java. 1.3. Applicazione della tecnica di memoization. - Motivi del'inefficienza della soluzione ricorsiva; - Parametro di stato: matrice "history" per rappresentare la storia computazionale; - Array bidimensionali in Java; - Ricorsione basata sullo stato; - Definizione del metodo base. 1.4. Tecnica di programmazione dinamica (dynamic programming). 2. Esercizi: - definizione di soluzioni ricorsive che applicano la tecnica di memoization per i numeri di Stirling di II specie; - realizzazione iterativa che applica la tecnica di programmazione dinamica; - riduzione dello spazio di memoria (da matrice a vettore) applicando la tecnica di programmazione dinamica al calcolo del numero di percorsi di Manhattan; - applicazione delle tecnicha di memoization e programmazione dinamica al calcolo dei coefficienti binomiali (matrice triangolare). Riferimenti: sulla tecnica di memoization vedi anche le prime sezioni del cap. 12 del libro di testo, in particolare la sez. 12.3. Per quanto riguarda il linguaggio Java, con riferimento al libro di Lewis & Loftus; i costrutti di controllo e le espressioni sono introdotti nel cap. 3, in particolare sezz. 3.1-3.2, 3.4-3.6 e 3.8; gli array nel cap. 6 e specificamente quelli multidimensionali nella sez. 6.4. *** Lezione 37 - 22/05/08 (Fabio) 1. Esempio rivisitato: Sottosequenza comune piu' lunga. 1.1. Richiamo della soluzione ricorsiva in Scheme. 1.2. Applicazione della tecnica di memoization (in Java). - Motivi del'inefficienza della soluzione ricorsiva; - Parametro di stato: analogie e differenze in relazione al problema dei percorsi di Manhattan; - Osservazione: i parametri delle chimate ricorsive sono sempre coppie di postfissi delle parole iniziali; - Parametro di stato: matrice indicizzata sulla base della coppia di lunghezze delle parole; - Ricorsione basata sullo stato: impostazione. 1.3. Modello di riferimento per la rappresentazione dello stato nell'esempio lcs( "arto", "astro" ): a s t r o +---+---+---+---+---+ 4 a | | | | | | +---+---+---+---+---+ 3 r | | | | | | +---+---+---+---+---+ 2 t | | | | | | +---+---+---+---+---+ 1 o | | | | | | +---+---+---+---+---+ 0 5 4 3 2 1 0 2. Discussione dal punto di vista degli strumenti di Java: 2.1. Aspetti sintattici. - Notazione relativa agli oggetti: .(); - Notazioni di uso corrente per array e String. 2.2. Metodo base per calcolare la sottosequenza comune piu' lunga applicando la tecnica di memoization. 2.3. Metodo di supporto: "longer" (vedi analoga procedura della soluzione in Scheme). 3. Applicazione della tecnica di programmazione dinamica. 4. Breve digressione sul confronto delle tecniche di memoization e dynamic programming. 4.1. Vantaggi della tecnica di memoization: - Vengono calcolati solo i valori che si rendono necessari. 4.2. Vantaggi della tecnica di programmazione dinamica: - Riduzione (leggera) dei tempi dovuti al controllo delle ricorsioni; - Potenzialita' di ulteriori compattamenti relativi all'uso della memoria (p. es. da matrice a vettore). 4.3. Considerazioni metodologiche. - Prima: soluzione logicamente chiara (p. es. ricorsiva), ragionando pervalentemente nel dominio del problema; - Poi: raffinamenti per migliorare le prestazioni, ragionando piu' a fondo sul modello computazionale; - Esempio: soluzione ricorsiva -> memoization -> programmazione dinamica -> riduzione dello spazio di memoria utilizzato; - Ricorsione e memoization: approccio risolutivo top-down; - Iterazione e programmazione dinamica: approccio bottom-up. 5. Esercizi: - codifica della procedura di inizializzazione dello stato; - sperimentazione e confronto delle prestazioni rispetto al caso ricorsivo (differenze nettamente percepibili!); - visualizzazione delle componenti della matrice effettivamente calcolate applicando la tecnica di memoization alla determinazione della sottosequenza comune piu' lunga; - formalizzazione della soluzione che adotta la tecnica di programmazione dinamica. Riferimenti: vedi anche il cap. 12 del libro di testo; in particolare, la tecnica di programmazione dinamica e' introdotta nella sez. 12.4 e confrontata con quella di memoization nella sez. 12.5. Per quanto riguarda il linguaggio Java, con riferimento al libro di Lewis & Loftus; gli array multidimensionali sono discussi nella sez. 6.4 e la classe String nella sez. 2.5.1. *** Lezione 38 - 23/05/08 (Fabio) 1. Altre soluzioni del problema della LCS: - Calcolo della LCS utilizzando tre matrici (int[][], boolean[][], boolean[][]). 2. Esercizio: - soluzione che utilizza solo una matrice di interi (!). *** Lezione 39 - 29/05/08 (Claudio) 1. Verifica formale della correttezza dei programmi. 1.1. Esplicitazione delle specifiche in relazione al problema: Precondizioni e postcondizioni; 1.2. Esplicitazione della logica algoritmica: Asserzioni e invarianti dei comandi iterativi; 1.3. Significato dei simboli nelle asserzioni e nel programma (un'asserzione "parla" dei valori delle variabili); 1.4. Terminazione delle computazioni iterative: Funzione di terminazione. 2. Passi della verifica: Correttezza parziale relativamente a un programma iterativo: 2.1. L'invariante vale all'inizio dell'iterazione; 2.2. L'invariante si conserva al generico passo iterativo; 2.3. Alla fine l'invariante implica la postcondizione. 3. Verifica della correttezza totale: Terminazione. 3.1. Funzione di terminazione di un comando iterativo. - Interpretazione intuitiva: stima per eccesso del numero di iterazioni. 3.2. Proprieta' delle funzioni di terminazione: - Immagine discreta; - Limite inferiore prefissato; - Decresce strettamente ad ogni passo iterativo. 4. Esempio di verifica formale della correttezza: Quadrato di un numero naturale come somma di numeri dispari. - Precondizioni: condizioni sui valori dei dati iniziali; - Postcondizioni: relazioni fra risultato e dati iniziali; - Invarianti dell'iterazione: valori e proprieta' che si conservano ad ogni passo iterativo; - Funzione di terminazione. 5. Esempio di verifica formale della correttezza: Algoritmo per il calcolo della potenza naturale di un numero naturale positivo; - Verifica della correttezza e comando di scelta: casi e assunzioni. 5. Esercizio: - Correttezza della moltiplicazione "del contadino Russo". Riferimenti: per approfondimenti vedi anche appunti sulla dimostrazione di correttezza dei programmi imperativi, riferiti nella pagina degli esempi. *** Lezione 40 - 30/05/08 (Fabio) 1. Esempio di classi in Java: Modello per il gioco del Sudoku. - Protocollo: costruttore e metodi read, write e clear; - Realizzazione senza controlli di correttezza dei dati introdotti; - Controllo della correttezza dei dati introdotti. Riferimenti: classi, oggetti, variabili di istanza e metodi sono introdotti nel cap. 4. del libro di Lewis & Loftus, in particolare vedi sezz. 4.1-4.5; vedi anche la discussione dell'attributo "static", applicato a variabili e metodi, nella sez. 5.2. L'astrazione basata sul concetto di oggetto e il paradigma object-oriented sono introdotti anche nei capitoli 13 e 14 del libro di testo, utilizzando il linguaggio Scheme, e nei capitoli 3, 4 e 5 del libro di Arnow & Weiss (in Java). *** Lezione 41 - 5/06/08 (Claudio) 1. Discussione: Ragionamento e analisi dei programmi imperativi. 1.1. Correttezza dei programmi imperativi: il problema e' ben posto se sono formalizzate le specifiche (correttezza rispetto a cosa? la correttezza non e' un concetto assoluto, ma relativo alle specifiche); 1.2. Aspetti pragmatici: testing e verifica nella fase di sviluppo e implementazione, documentazione ai fini dell'utilizzo e della manutenzione; 1.3. Strumenti per esprimere asserzioni relative ai programmi in Java: Jass, JML. 2. Utilita' di asserzioni e invarianti (dell'iterazione, di classe). 2.1. L'impiego di due linguaggi formali diversi (sia per la struttura sia per gli aspetti su cui si focalizzano) al fine esprimere la logica dei programmi favorisce l'emergere di errori o incoerenze. - Linguaggio di programmazione: attenzione rivolta prevalentemente agli aspetti dinamici della computazione (come lo stato cambia); - Linguaggio di specifica: attenzione rivolta prevalentemente alle regolarita' (relazioni fra i valori che definiscono lo stato). 2.2. L'abitudine a ragionare sulle computazioni e sulle rappresentazioni in termini di invarianti contribuisce a sviluppare quelle attitudini che consentono di scrivere codice meglio organizzato e piu' elegante. 2.3. Le asserzioni e gli invarianti costituiscono una documentazione precisa e condivisa (il linguaggio formale rispetta convenzioni) della logica del programma. 2.4. Le asserzioni e gli invarianti sono espresse in un linguaggio "operativo" (come i linguaggi di programmazione), che attraverso opportuni strumenti consente di controllare meglio il processo di sviluppo del software. - Strumenti per verificare in tempo di esecuzione se le asserzioni sono soddisfatte (JML, Jass); - Strumenti di supporto alla dimostrazione formale di correttezza (piu' complessi, non oggetto di questo corso). (Normalmente le funzioni di documentazione e verifica operativa sono svolte contemporaneamente: la sintassi dei linguaggi di specifica si presenta come commento dal compilatore Java, funzione di documentazione, mentre determina la produzione di codice di verifca automatizzata attraverso l'uso di interpreti del linguaggio di specifica, strumenti utilizzati nelle fasi di realizzazione e testing.) 3. Linguaggio "Jass" per formalizzare asserzioni come commenti dei programmi in Java. - Riferimenti e modalita' di utilizzo; - Utilizzo ed effetti del codice generato dal precompilatore Jass. 4. Struttura sintattica del linguaggio Jass. 4.1. Commenti speciali: /** ... **/; 4.2. Collocazione delle asserzioni nel testo del programma e parole riservate. - Precondizioni: require; - Postcondizioni: ensure, Result; - Invarianti e funzioni di terminazione: invariant, variant; - Quantificatori universale ed esistenziale limitati: forall / exists _ : { _ .. _ } # _ - Asserzioni generiche: check. 5. Esempio: Formalizzazione in Jass delle asserzioni relative ai programmi per il calcolo del quadrato come somma di numeri dispari e del prodotto (algoritmo del contadino Russo). - Precondizioni e postcondizioni; - Invarianti di ciclo e funzione di terminazione. 6. Esempio: Formalizzazione in Jass delle asserzioni relative a un programma per la ricerca dicotomica di un valore in un array. - Assunzioni: array ordinato che contiene il valore cercato; - Precondizioni e postcondizioni; - Invarianti di ciclo e funzione di terminazione. Riferimenti: per approfondimenti vedi anche appunti sulla dimostrazione di correttezza dei programmi imperativi, riferiti nella pagina degli esempi. *** Fine del corso ***