Esercizio: punti e segmenti! Punti e segmenti nel piano, con data abstraction Esercizio: punti e segmenti! Punti e segmenti nel piano, con data abstraction! Lunghezza di un segmento l = (x 2 " x 1 ) 2 + (y 2 " y 1 ) 2 y 2 y 1 x 1 x 2!! Data abstraction:! Segmento " Costruttore make-seg pt1 pt2 " Selettori start-point seg end-point seg! Punto " Costruttore make-point x y " Selettori x-coor point y-coor point 1
Esercizio: punti e segmenti (define (segment-length segment) (let ((dx (- (x-coord (end-point segment)) (x-coord (start-point segment)))) (dy (- (y-coord (end-point segment)) (y-coord (start-point segment))))) (sqrt (+ (square dx) (square dy))))) Implementazione segmento (define (make-seg pt1 pt2) (cons pt1 pt2)) (define (start-point seg) (car seg)) (define (end-point seg) (cdr seg)) Implementazione punto (coordinate rettangolari) (define (make-point x y) (cons x y)) (define (x-coor point) (car point)) (define (y-coor point) (cdr point)) Esercizio: punti e segmenti! Cambiamo la rappresentazione dei punti e usiamo la rappresentazione polare p=(r,!)! I programmi di alto livello (es. segment-length) non cambiano: usano costruttori e selettori con gli stessi nomi x = r cos" y = r sin" r!! (define (make-point r ang) (cons r ang)) (define (x-coor p) (* (car p) (cos (cdr p)))) (define (y-coor p) (* (car p) (sin (cdr p)))) 2
Che cosa intendiamo per dati?! I dati sono definiti da una collezione di costruttori e selettori, insieme a specifiche condizioni che queste procedure devono soddisfare! Data abstraction interviene a tutti i livelli! Es. Non abbiamo mai detto cosa sia una coppia, ma soltanto che avevamo le procedure cons, car e cdr per operare sulle coppie Che cosa intendiamo per dati?! Riguardo le coppie, l unica cosa che ci interessa sapere è che (car (cons x y)) => x (cdr (cons x y)) => y! cons, car, e cdr sono primitive nello Scheme! Tuttavia qualunque tripla di procedure che soddisfa le precedenti condizioni, può essere usata per implementare le coppie 3
Che cosa intendiamo per dati?! Per esempio, possiamo implementare le coppie, senza usare nessuna struttura dati, ma soltanto le procedure (define (cons x y) (lambda (pick) (cond ((= pick 1) x) ((= pick 2) y) (else (error "L'argomento non e' ne' 1 ne' 2 )))) ) (define (car z) (z 1)) (define (cdr z) (z 2)) Che cosa intendiamo per dati? (define (cons x y) (lambda (pick) (cond ((= pick 1) x) ((= pick 2) y) (else (error "L'argomento non e' ne' 1 ne' 2 )))) ) (define (car z) (z 1)) (define (cdr z) (z 2)) (cons 6 9) => (lambda (pick) (cond ((= pick 1) 6) ((= pick 2) 9) (else (error "L'argomento non e' ne' 1 ne' 2 )))) 4
Cons, car, cdr in versione procedurale! Esiste una versione ancora più elegante di implementare le coppie proceduralmente (define (cons x y) (lambda (m) (m x y))) (define (car z) (z (lambda (p q) p))) Esercizio: verificare che funzionano con il modello di sostituzione (define (cdr z) (z (lambda (p q) q))) Che cosa intendiamo per dati?! L uso di queste procedure appena viste è quanto di più lontano ci sia dalla nostra intuizione di dato! Tuttavia possiamo verificare che soddisfano le condizioni date per cons, car e cdr! Se usiamo queste procedure, esse sono indistinguibili da quelle che usano strutture dati reali! La rappresentazione procedurale dei dati gioca un ruolo fondamentale nelle techniche di programmazione avanzata, come l object-oriented! In particolare quanto visto prima, è un esempio di object-oriented di stile message-passing 5
Visualizzare le coppie! Abbiamo visto che le coppie (pairs) costituiscono una sorta di colla per costruire oggetti composti complessi! La notazione standard per visualizzare le coppie - ad es. (cons 1 2) - è chiamata notazione box-and-pointer (cons 1 2) 1 2 (car (cons 1 2)) " 1 (cdr (cons 1 2)) " 2 Dati gerarchici e proprietà di chiusura! Abbiamo visto che cons può essere usato per combinare coppie! Le coppie costituiscono una sorta di mattone universale per costruire ogni sorta di strutture dati (cons (cons 1 2) (cons 3 4)) 3 4 1 2 6
Dati gerarchici e proprietà di chiusura (cons (cons 1 (cons 2 3)) 4)) 4 1 2 3! La capacità di creare coppie i cui elementi sono altre coppie è l essenza dell importanza della struttura a lista! Questa capacità si chiama proprietà di chiusura del cons! cons può prendere coppie in input e produce coppie in output Proprietà di chiusura! La parola chiusura viene dall algebra astratta! Un insieme di elementi si dice chiuso secondo una certa operazione, se applicando l operazione agli elementi dell insieme, si ottengono elementi che appartengono ancora all insieme 7
Proprietà di chiusura! La proprietà di chiusura ci permette di creare strutture gerarchiche! Strutture gerarchiche: strutture composte di parti che a loro volta sono composte di parti ecc! Le procedure composte sono ancora procedure! I dati composti sono ancora dati! Vedremo alcune tecniche standard per rappresentare sequenze (liste) e alberi, usando le coppie Rappresentare le sequenze (liste)! Una sequenza è una collezione ordinata di oggetti! Un modo semplice di rappresentare le sequenze in termini di coppie è usando una catena di coppie (cons 1 (cons 2 (cons 3 (cons 4 nil)))) 1 2 3 4 8
Rappresentare le sequenze (liste)! Il car di ciascuna coppia corrisponde ad un elemento della catena! Il cdr di una coppia è la coppia successiva nella catena! Il cdr della coppia finale segnala la fine della catena: " nella rappresentazione box-and-pointer è indicato come un barra / " nei programmi con la variabile nil " nil può essere pensato come la sequanza di nessun elemento (cioè la lista vuota) Liste! Lo Scheme fornisce una procedura primitiva list che è il costruttore delle liste! La lista vista prima può essere costruita con (list 1 2 3 4)! In generale (list <el 1 > <el 2 > <el n >)! È equivalente a (cons <el 1 > (cons <el 2 > (cons (cons <el n > nil) )))! (list) ritorna la lista vuota: (define nil (list))! I sistemi Scheme stampano le liste come sequenze di elementi (list 1 2 3 4) " (1 2 3 4) 9
Operazioni sulle liste! Tramite car e cdr possiamo scrivere procedure che manipolano le liste! Per esempio, scriviamo una procedura list-ref che prende due argomenti: 1. una lista 2. un numero n! e ritorna l n-esimo elemento della lista! Il primo elemento è in posizione 0 Operazioni sulle liste (define (list-ref items n) (if (= n 0) (car items) ;per n=0 torna il car della lista (list-ref (cdr items) (- n 1)))) ;altrimenti torna l (n-1) esimo elemento del cdr della lista (define lista-quadrati (list 1 4 9 16 25)) (list-ref lista-quadrati 3) "16 3 2 1 0 10
Operazioni sulle liste! Spesso dobbiamo scandire la lista tramite il cdr! Lo Scheme fornisce una procedura primitiva null? che testa se il suo argomento è la lista vuota (null? nil) " #t (null? (list 1 2)) " #f Esempio: lunghezza di una lista (define (length items) (if (null? items) 0 ;la lunghezza della lista vuota è 0 (+ 1 (length (cdr items))))) ;altrimenti la lunghezza è 1 più la lunghezza del cdr (define dispari (list 1 3 5 7)) (length dispari) " 4 11
Esempio: lunghezza di una lista! Riconosciamo nella definizione di length il classico piano dell induzione " la lunghezza della lista vuota è 0 " la lunghezza di una lista è 1 più la lunghezza del cdr della lista! Possiamo anche definire length in stile iterativo (define (length items) (define (length-iter a count) (if (null? a) count (length-iter (cdr a) (+ 1 count)))) (length-iter items 0)) Esempio: append! Un altra tecnica comune è quello di costruire nuove liste con una successione di cons! Es. La procedura append prende due liste come argomenti, e da queste forma una nuova lista (append lista-quadrati dispari) " (1 4 9 16 25 1 3 5 7) (append dispari lista-quadrati) " (1 3 5 7 1 4 9 16 25) 12
Esempio: append! Per implementare append usiamo ancora un piano ricorsivo:! Fai append di list1 list2 " se list1 è la lista vuota, allora il risultato è list2 " altrimenti, fai append del cdr di list1 con list2, e fai cons del car di list1 con questo risultato (define (append list1 list2) (if (null? list1) list2 (cons (car list1) (append (cdr list1) list2)))) Esercizi:last-pair e reverse (define (last-pair list) ;restituisce l ultimo pair di una lista (if (null? (cdr list)) list (last-pair (cdr list)))) (define (reverse items) (if (null? items) nil (append (reverse (cdr items)) (list (car items))))) (define (reverse items) ;reverse in versione iterativa (define (rev-iter items acc) (if (null? items) acc (rev-iter (cdr items) (cons (car items) acc)))) (rev-iter items nil)) 13