Scaricare la presentazione
La presentazione è in caricamento. Aspetta per favore
PubblicatoIvo Poli Modificato 9 anni fa
1
1 Un esempio: le liste ordinate di interi
2
2 Liste ordinate OrderedIntList 4 lista ordinata di interi –modificabile
3
3 Specifica di OrderedIntList 1 public class OrderedIntList { // OVERVIEW: una OrderedIntList è una lista // modificabile di interi ordinata // tipico elemento: [x1,..., xn], xi<xj se i<j // costruttore public OrderedIntList () // EFFECTS: inizializza this alla lista vuota // metodi public void addEl (int el) throws DuplicateException // MODIFIES: this // EFFECTS: aggiunge el a this, se el non occorre in // this, altrimenti solleva DuplicateException public void remEl (int el) throws NotFoundException // MODIFIES: this // EFFECTS: toglie el da this, se el occorre in // this, altrimenti solleva NotFoundException
4
4 Specifica di OrderedIntList 2 public boolean isIn (int el) // EFFECTS: se el appartiene a this ritorna // true, altrimenti false public boolean isEmpty () // EFFECTS: se this è vuoto ritorna true, altrimenti // false public int least () throws EmptyException // EFFECTS: se this è vuoto solleva EmptyException // altrimenti ritorna l’elemento minimo in this public boolean repOk () public String toString () // metodi test standard }
5
5 Specifica di OrderedIntList 3 public class OrderedIntList { public OrderedIntList () public void addEl (int el) throws DuplicateException public void remEl (int el) throws NotFoundException public boolean isIn (int el) public boolean isEmpty () public int least () throws EmptyException public boolean repOk () public String toString ()} DuplicateException e NotFoundException checked
6
6 Come implementarla? 4 Potremmo usare una struttura dati lineare: lista concatenata 4 lista concatenata: definizione ricorsiva 4 o e’ vuota 4 o e’ formata da un nodo, che contiene un valore (di tipo int) ed un puntatore al resto della lista 4 bisognerebbe mantenere i nodi ordinati in base al valore associato in ordine crescente (tipo esercizio di LIP)
7
7 Usiamo invece un albero binario 4 albero binario: struttura ricorsiva tipo lista 4 o e’ vuoto 4 o contiene un nodo (detto radice) a cui e’ associato un valore (in questo caso di tipo int) ed un puntatore a due alberi, detti sottoalbero sinistro e destro rispettivamente 4 Nota: si chiama binario esattamente perche’ ogni nodo ha due figli (si puo’ generalizzare)
8
8 Esempio 4 6126 10 5 6 6 8 6 1 12 2 4 I nodi che puntano a sottoalberi vuoti si dicono foglie 4 Il nodo radice contiene 4
9
9 Commenti 4 Rispetto ai tipi di dato lineari, lista doppia –si può percorrere da ogni elemento in avanti o all’indietro 4 Mediamente piu’ efficiente la ricerca se i valori sono inseriti in modo opportuno 4 Complessita’ degli algoritmi di inserimento, ricerca, eliminazione etc… (LSD, Algoritimica)
10
10 Ordinamento 4 Per mantenere l’ordinamento richiediamo che, per ogni nodo a cui e’ associato n 4 tutti i valori contenuti nel sottoalbero sinistro abbiano valore strettamente minore di n, 4 tutti i valori contenuti nel sottoalbero destro abbiano valore strettamente maggiore di n.
11
11 Vantaggio 4 Se leggiamo i valori effettuando la visita dell’albero in ordine simmetrico otteniamo I valori ordinati in modo crescente 4 visita simmetrica: in modo ricorsivo, visitiamo prima il sottoalbero sinistro, poi la radice, poi il sottoalbero destro 4 Un esempio
12
12 Come lo implementiamo? public class OrderedIntList { // OVERVIEW: una OrderedIntList è una lista // modificabile di interi ordinata // tipico elemento: [x1,..., xn], xi<xj se i<j private boolean vuota; private OrderedIntList prima, dopo; private int val; 4 la rep contiene –una variabile boolean che ci dice se la lista è vuota –la variabile intera che contiene l’eventuale valore dell’elemento –due (puntatori a) OrderedIntList s che contengono la lista di quelli minori e quelli maggiori, rispettivamente 4 implementazione ricorsiva
13
13 Come è fatta ? La lista vuota ha la variabile boolean true Le foglie puntano alla lista vuota Notate l’ordinamento 12F F 4 F FF T 819 17 5 TF T T T T T
14
14 Commenti 4 L’invariante di rappresentazione e la funzione di astrazione descrivono le caratteristiche dell’implementazione che abbiamo in mente 4 Dipendono dalla rappresentazione e devono essere inserite come commenti alla rappresentazione 4 Sono fondamentali per ragionare sulla correttezza dell’implementazione o per capire l’implementazione (se per esempio qualcuno la dovesse modificare)
15
15 private boolean vuota; private OrderedIntList prima, dopo; private int val; I(c) = c.vuota oppure (c.prima != null e c.dopo != null e I (c.prima) e I (c.dopo) e (!c.prima.isEmpty() -> c.prima.max() < c.val) e (!c.dopo.isEmpty() -> c.dopo.least() > c.val) ) 4 l’invariante esprime le proprieta’ che devono soddisfare gli oggetti concreti, ovvero le loro variabili d’istanza 4 In questo caso il ruolo di vuota e la relazione tra i valori contenuti nella radice e nei sottoalberi (necessarie per garantire l’ordinamento richiesto) Invariante
16
16 private boolean vuota; private OrderedIntList prima, dopo; private int val; (c) = se c.vuota allora [], altrimenti (c) = (c.prima) + [c.val] + (c.dopo) 4 Mappa gli oggetti concreti, implementati con un albero binario, nella corrispondente lista ordinata in modo crescente, tipo [x1,..., xn], xi<xj se i<j 4 La funzione di astrazione ricorsiva realizza la lettura dei valori in modo simmetrico (dato che la lista deve essere ordinata) Funzione di astrazione
17
17 Esempio 4 Oggetto concreto x quello della figura 4 Oggetto astratto corrispondente e’ la seguente lista (x)= [4,5,12,17,19]
18
18 Nota 4 Oggetto concreto, implementato tramite un albero binario, non e’e non deve essere visibile a chi usa OrderedIntList Oggetto astratto ( (this) ) e’ visibile tramite il metodo toString () che implementa la funzione di astrazione 4 Fondamentale se vogliamo conoscere lo stato dell’oggetto su cui stiamo lavorando, pur non avendo accesso diretto alla rappresentazione 4 Permette di fare delle verifiche sul comportamento dei metodi (creo un oggetto, inserisco dei valori, vedo se il risultato e’ quello atteso)
19
19 Domanda 4 Se abbiamo due implementazioni diverse (una fatta con l’albero binario e l’altra con una lista concatenata) 4 Diverse invarianti e f. di astrazione 4 Ma gli oggetti astratti devono essere dello stesso tipo, quello descritto nella OVERVIEW [x1,..., xn], xi<xj se i<j 4 Da fuori vediamo solo gli oggetti astratti perche’ non vediamo differenze tra le due implementazioni (a parte chiaramente che possono differire in efficienza) 4 Principio di base del tipo di dato astratto: la specifica e’ l’interfaccia con l’esterno e maschera l’implementazione
20
20 Esercizio 4 Fate l’implementazione con lista concatenata (tipo LIP) 4 Formalizzate la funzione di astrazione e l’invariante 4 Verificate che l’invariante valga 4 Verificate la correttezza dei metodi 4 Dal punto di vista di chi usa il tipo di dato non deve vedersi differenza
21
21 (c) = (c.prima) + [5] + (c.dopo) = ([]+ 4]+[]) + [5] + ([]+ 8]+[])= [4, 5, 8] 4 Funzione di astrazione per il sottoalbero con radice 5 4 Se l’oggetto concreto non soddisfa l’invariante (che impone l’ordinamento) pero’ non e’ detto che troviamo I valori ordinati nel modo richiesto 4 Facciamo vedere che l’invariante impone dei vincoli sufficienti a garantire le proprieta’ dei dati richieste nella specifica Esempio
22
22 4 Facciamo vedere per induzione sulla lunghezza della lista che Se I(c) allora (c) =[x1,..., xn], xi<xj se i<j Caso Base: vale c.vuota. Quindi (c) = []. La lista vuota [] e’ ordinata. Invariante e Dati Astratti
23
23 Caso Induttivo: non vale c.vuota, ma (c.prima != null e c.dopo != null e I (c.prima) e I (c.dopo) e (!c.prima.isEmpty() -> c.prima.max() < c.val) e (!c.dopo.isEmpty() -> c.dopo.least() > c.val) ) Abbiamo (c) = (c.prima) + [c.val] + (c.dopo)= [y1,..., yn]+ [c.val] + [z1,..., zk] Dato che c.prima che c.dopo sono piu’ piccole di c, e che soddisfano l’invariante, allora per ipotesi induttiva [y1,..., yn] e [z1,..., zk] sono ordinate in modo crescente 4 Le altre proprieta’ dell’invariante garantiscono che sia anche tutta ordinata Invariante
24
24 4 L’invariante cattura le proprieta’ richieste nella specifica dei dati, in particolare l’ordinamento degli interi crescente (l’abbiamo fatto vedere) 4 Rimangono da implemenatre i metodi in modo che preservino l’invariante 4 Discutiamo anche la loro correttezza rispetto alla specifica, assumendo che l’invariante valga, e utilizzando la funzione di astrazione per collegare la specifica che parla di oggetti astratti e l’implementazione che si riferisce agli oggetti concreti Invariante
25
25 Implementazione public class OrderedIntList { // OVERVIEW: una OrderedIntList è una lista // modificabile di interi ordinata // tipico elemento: [x1,..., xn], xi<xj se i<j private boolean vuota; private OrderedIntList prima, dopo; private int val; // costruttore public OrderedIntList () // EFFECTS: inizializza this alla lista vuota { vuota = true; } il costruttore inizializza solo la variabile vuota l’oggetto prodotto soddisfa l’invariante ( c.vuota = true ) verifica la propria specifica ( (c) = [] )
26
26 Inserire e rimuovere 4 Difficili perche’ bisogna preservare l’ordinamento 4 Le proprieta’ dell’ordinamento sono espresse dall’invariante (ci aiuta a non fare errori in una situazione un po’ complessa) 4 Vediamo un esempio
27
27 public void addEl (int el) throws DuplicateException // MODIFIES: this // EFFECTS: aggiunge el a this, se el non occorre in // this, altrimenti solleva DuplicateException {if (vuota) { prima = new OrderedIntList(); dopo = new OrderedIntList(); val = el; vuota = false; return; } if (el == val) throw new DuplicateException(“OrderedIntList.addEl”); if (el < val) prima.addEl(el); else dopo.addEl(el); } 4 Ricorsiva, inserisce rispettando l’ordinamento 4 Propaga automaticamente l’eventuale eccezione sollevata dalle chiamate ricorsive Inserire un elemento
28
28 Esempio 4 vediamo la lista prodotta dalla sequenza di comandi OrderedIntList ls = new OrderedIntList(); ls.addEl(12); ls.addEl(5); ls.addEl(17); ls.addEl(4);ls.addEl(8); ls.addEl(19); 12F F 4 F FF T 819 17 5 TF T T T T T
29
29 Invariante: per casi I(c) = c.vuota oppure (c.prima != null e c.dopo != null e I(c.prima) e I(c.dopo) e (!c.prima.isEmpty() -> c.prima.max() < c.val) e (!c.dopo.isEmpty() -> c.dopo.least() >= c.val) ) public void addEl (int el) throws DuplicateException {if (vuota) { prima = new OrderedIntList(); dopo = new OrderedIntList(); val = el; vuota = false; return; }... prima != null e dopo != null (calcolati dal costruttore) I (prima) e I (dopo) (calcolati dal costruttore) le implicazioni sono vere perché la premessa è falsa
30
30 Invariante: this non vuoto I(c) = c.vuota oppure (c.prima != null e c.dopo != null e I(c.prima) e I(c.dopo) e (!c.prima.isEmpty() -> c.prima.max() < c.val) e (!c.dopo.isEmpty() -> c.dopo.least() >= c.val) ) public void addEl (int el) throws DuplicateException... if (el < val) prima.addEl(el); else dopo.addEl(el); } 4 prima != null e dopo != null (this non e’ vuoto) I (prima) e I (dopo) (calcolati da una chiamata ricorsiva su una lista piu’ piccola) ramo then: il nuovo massimo di prima è (induttivamente) minore di val ramo else: il nuovo minimo di dopo è (induttivamente) maggiore di val
31
31 Specifica (c) = se c.vuota allora [], altrimenti (c.prima) + [c.val] + (c.dopo) public void addEl (int el) throws DuplicateException // MODIFIES: this // EFFECTS: aggiunge el a this, se el non occorre in // this, altrimenti solleva DuplicateException {if (vuota) { prima = new OrderedIntList(); dopo = new OrderedIntList(); val = el; vuota = false; return; }... (c pre ) = [] (c.prima) = [] e (c.dopo) = [] e c.val] = [el] (c) = [el] (ha inserito l’elemento)
32
32 Specifica public class OrderedIntList { private boolean vuota; private OrderedIntList prima, dopo; private int val; // (c) = se c.vuota allora [], altrimenti // (c.prima) + [c.val] + (c.dopo) public void addEl (int el) throws DuplicateException // MODIFIES: this // EFFECTS: aggiunge el a this, se el non occorre in // this, altrimenti solleva DuplicateException... if (el == val) throw new DuplicateException(“OrderedIntList.addEl”); 4 se ci sono elementi duplicati solleva l’eccezione, eventualmente propagando eccezioni sollevate dalle chiamate ricorsive (vedi dopo)
33
33 Specifica // (c) = se c.vuota allora [], altrimenti // (c.prima) + [c.val] + (c.dopo) public void addEl (int el) throws DuplicateException // MODIFIES: this // EFFECTS: aggiunge el a this, se el non occorre in // this, altrimenti solleva DuplicateException... if (el < val) prima.addEl(el); else dopo.addEl(el); } 4 (c pre ) = (c.prima pre ) + [c.val] + (c.dopo pre ) se el < val la chiamata ricorsiva solleva l’eccezione oppure produce (c.prima) = aggiunge el a prima pre 4 (c.dopo) = (c.dopo pre ) 4 (c) = aggiunge el a c pre 4 Analogo nel caso simmetrico
34
34 Rimuovere un elemento 4 Difficile, bisogna mantenere l’ordinamento 4 Supponiamo di dovere rimuovere 12 (e’ la radice) 4 Non possiamo semplicemente spostare nella radice uno dei nodi figli (ex. 5)
35
35 Idea 4 si prende il valore minimo del dopo (se c’e’) e si mette al posto della radice (e si rimuove dal dopo) 4 e’ di sicuro piu’ grande di tutti i valori di prima e piu’ piccolo di quelli di dopo 4 se dopo e’ vuoto allora si ricopia nel nodo radice il sottoalbero prima 4 in questo modo viene preservato l’ordinamento imposto dall’ordinamento
36
36 Implementazione di OrderedIntList 5 public void remEl (int el) throws NotFoundException // MODIFIES: this // EFFECTS: toglie el da this, se el occorre in // this, altrimenti solleva NotFoundException {if (vuota) throw new NotFoundException(“OrderedIntList.remEl”); if (el == val) try { val = dopo.least(); dopo.remEl(val); } catch (EmptyException e) { vuota = prima.vuota; val = prima.val; dopo = prima.dopo; prima = prima.prima; return;} else if (el < val) prima.remEl(el); else dopo.remEl(el); }
37
37 Implementazione di OrderedIntList 6 public boolean isIn (int el) // EFFECTS: se el appartiene a this ritorna // true, altrimenti false {if (vuota) return false; if (el == val) return true; if (el < val) return prima.isIn(el); else return dopo.isIn(el); } public boolean isEmpty () // EFFECTS: se this è vuoto ritorna true, altrimenti false {return vuota; } Si ricerca in base all’ordinamento (efficiente, non si visita tutto)
38
38 Implementazione di OrderedIntList 6.1 public class OrderedIntList { private boolean vuota; private OrderedIntList prima, dopo; private int val; // (c) = se c.vuota allora [], altrimenti // (c.prima) + [c.val] + (c.dopo) public boolean isIn (int el) // EFFECTS: se el appartiene a this ritorna // true, altrimenti false {if (vuota) return false; if (el == val) return true; if (el < val) return prima.isIn(el); else return dopo.isIn(el); } public boolean isEmpty () // EFFECTS: se this è vuoto ritorna true, altrimenti false {return vuota; } 4 dimostrazioni di correttezza ovvie, visto che l’invariante che garantisce l’ordinamento
39
39 Implementazione di OrderedIntList 7.1 public class OrderedIntList { private boolean vuota; private OrderedIntList prima, dopo; private int val; public int least () throws EmptyException // EFFECTS: se this è vuoto solleva EmptyException // altrimenti ritorna l’elemento minimo in this {if (vuota) throw new EmptyException(“OrderedIntList.least”); try { return prima.least(); } catch (EmptyException e) {return val;} } 4 dimostrazione di correttezza ovvia usando l’invariante, che garantisce l’ordinamento Ritorna proprio il primo elemento di (this)
40
40 Implementazione di OrderedIntList 7.1 public class OrderedIntList { private boolean vuota; private OrderedIntList prima, dopo; private int val; public String toString(){ // EFFECTS:standard {if (vuota) return “”; return prima.toString() + val + dopo.toString(); } } 4 dimostrazione di correttezza ovvia usando l’invariante, che garantisce l’ordinamento Implementa (this) visita in ordine simmetrico
Presentazioni simili
© 2024 SlidePlayer.it Inc.
All rights reserved.