Astrazioni sui dati Ragionare sui Tipi di Dato

  • Slides: 42
Download presentation
Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti 1

Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti 1

Correttezza dell’implementazione 4 L’implementazione del tipo di dato soddisfa la specifica 4 Rispetto al

Correttezza dell’implementazione 4 L’implementazione del tipo di dato soddisfa la specifica 4 Rispetto al caso delle procedure stand-alone piu’ complicato 1. 2. Specifica: descrizione astratta degli oggetti + metodi pubblici e le loro proprieta’ Implementazione: rappresentazione + implementazione dei metodi 2

Correttezza dell’implementazione Bisogna provare due cose: 1. La rappresentazione (implementazione) dei dati soddisfa le

Correttezza dell’implementazione Bisogna provare due cose: 1. La rappresentazione (implementazione) dei dati soddisfa le proprieta’ richieste nella OVERVIEW 2. I metodi soddisfano le loro specifiche (simile al caso delle procedure stand-alone) 3

Correttezza dell’implementazione Le due cose sono pero’ strettamente correlate 1. La correttezza dei metodi

Correttezza dell’implementazione Le due cose sono pero’ strettamente correlate 1. La correttezza dei metodi puo’ dipendere da proprieta’ della rappresentazione 1. La correttezza della rappresentazione dipende dai metodi, dato che i metodi creano e modificano gli oggetti 4

Servono altre informazioni 4 Funzione di astrazione: collega la rappresentazione degli oggetti con la

Servono altre informazioni 4 Funzione di astrazione: collega la rappresentazione degli oggetti con la loro descrizione astratta 4 Invariante di rappresentazione: specifica proprieta’ della rappresentazione degli oggetti 5

Primo problema 4 se vogliamo dimostrare che le implementazioni dei metodi soddisfano le rispettive

Primo problema 4 se vogliamo dimostrare che le implementazioni dei metodi soddisfano le rispettive specifiche 4 l’implementazione utilizza la rappresentazione – nel caso di Int. Set – private Vector els; 4 le specifiche esprimono proprietà dei dati astratti, ovvero degli insiemi – nel caso di Int. Set public boolean is. In (int x) // EFFECTS: se x appartiene a this ritorna // true, altrimenti false 4 Per sapere se l’implementazione la soddisfa devo sapere come è rappresentato un insieme 6

Cosa manca 4 Un modo per mettere in relazione tra loro i due “insiemi

Cosa manca 4 Un modo per mettere in relazione tra loro i due “insiemi di valori” concreti ed astratti 4 valori concreti: stati degli oggetti della classe (espressi da un insieme di var d’istanza) 4 valori astratti: il tipo di dato specificato 4 nel caso di Int. Set: devo collegare il vettore els all’insieme di interi che rappresenta 7

La funzione di astrazione 1 4 la funzione di astrazione cattura l’intenzione del progettista

La funzione di astrazione 1 4 la funzione di astrazione cattura l’intenzione del progettista nello scegliere una particolare rappresentazione 4 la funzione di astrazione a : C --> A porta da uno stato concreto – lo stato di un oggetto della classe C a uno stato astratto – lo stato dell’oggetto astratto 8

Esempio public class Int. Set { // OVERVIEW: un Int. Set è un insieme

Esempio public class Int. Set { // OVERVIEW: un Int. Set è un insieme modificabile // di interi di dimensione qualunque private Vector els; // la rappresentazione 4 a deve mappare vettori di interi in insiemi 4 a ([1, 2]) = {1, 2} 4 a ([3, 1]) = {1, 3} 4 a ([1, 3, 2]) = {1, 2, 3} 9

La funzione di astrazione 2 4 la funzione di astrazione è generalmente una funzione

La funzione di astrazione 2 4 la funzione di astrazione è generalmente una funzione molti-a-uno public class Int. Set { // OVERVIEW: un Int. Set è un insieme modificabile // di interi di dimensione qualunque private Vector els; // la rappresentazione – piú stati concreti (vettori di interi) vengono portati nello stesso stato astratto (insieme ) – a ([1, 2]) = {1, 2} – a ([2, 1]) = {1, 2} 10

Come definire la funzione di astrazione? 4 il problema è che non abbiamo una

Come definire la funzione di astrazione? 4 il problema è che non abbiamo una rappresentazione esplicita dei valori astratti (solo specifica informale) 4 l’oggetto da rappresentare può essere descritto da un oggetto astratto tipico della classe dato nella OVERVIEW della specifica. 4 per dare la funzione possiamo usare la notazione matematica e la notazione del linguaggio di programmazione con opportune abbreviazioni. 11

La funzione di astrazione di Int. Set public class Int. Set { // OVERVIEW:

La funzione di astrazione di Int. Set public class Int. Set { // OVERVIEW: un Int. Set è un insieme modificabile // di interi di dimensione qualunque // un tipico Int. Set è {x 1, , xn } private Vector els; // la rappresentazione a(c) = { c. els. get(i). int. Value() | 0 <= i < c. els. size() } C metavariabile che rappresenta un oggetto generico di Int. Set 12

La funzione di astrazione di Poly public class Poly { // OVERVIEW: un Poly

La funzione di astrazione di Poly public class Poly { // OVERVIEW: un Poly è un polinomio a // cofficienti interi non modificabile // un tipico Poly: c 0 + c 1*x + c 2*x 2 +. . . private int[] termini; // la rappresentazione private int deg; // la rappresentazione 4 rappresentiamo il polinomio con – array che ha la dimensione del grado piu’ uno – l’elemento in posizione n contiene il coefficiente del termine che ha esponente n (eventualmente 0) – deg contiene il grado del polinomio 4 La funzione di astrazione collega l’array al polinomio 13

La funzione di astrazione di Poly public class Poly { // OVERVIEW: un Poly

La funzione di astrazione di Poly public class Poly { // OVERVIEW: un Poly è un polinomio a // cofficienti interi non modificabile // un tipico Poly: c 0+ c 1 x + c 2 x 2 +. . . private int[] termini; // la rappresentazione private int deg; // la rappresentazione a(c) = c 0+ c 1 x + c 2 x 2 +. . . tale che ci = c. termini[i] se 0 <= i < c. termini. length = 0 altrimenti 4 notare che il valore di deg non ha nessuna influenza sulla funzione di astrazione – è una informazione derivabile dall’array termini che utilizziamo nello stato concreto per questioni di efficienza 14

Un altro problema 4 non tutti gli stati concreti “rappresentano” correttamente uno stato astratto

Un altro problema 4 non tutti gli stati concreti “rappresentano” correttamente uno stato astratto 4 infatti gli oggetti concreti soddisfano delle proprieta’, sono costruiti in modo opportuno 4 queste proprieta’ servono tipicamente sia per la correttezza della rappresentazione sia per quella dei metodi 4 Da cosa derivarle 15

Esempio public class Int. Set { // OVERVIEW: un Int. Set è un insieme

Esempio public class Int. Set { // OVERVIEW: un Int. Set è un insieme modificabile // di interi di dimensione qualunque // un tipico Int. Set è {x 1, , xn } private Vector els; // la rappresentazione // la funzione di astrazione // a(c) = { c. els. get(i). int. Value() | 0 <= i < c. els. size() } 4 il vettore els potrebbe contenere più occorrenze dello stesso elemento o contenere valori non di tipo Integer – questo sarebbe coerente con la funzione di astrazione – ma non rispecchierebbe la nostra scelta di progetto (riflessa nell’implementazione dei metodi , vedi per esempio size, remove) 16

L’invariante di rappresentazione 4 l’invariante di rappresentazione (rep invariant) è un predicato I :

L’invariante di rappresentazione 4 l’invariante di rappresentazione (rep invariant) è un predicato I : C --> boolean che è vero per gli stati concreti che sono rappresentazioni legittime di uno stato astratto 4 esprime le proprieta’ fondamentali della rappresentazione (implementazione) degli oggetti in base alle quali abbiamo definito i metodi 17

L’invariante di rappresentazione 4 l’invariante di rappresentazione, insieme alla funzione di astrazione, riflette le

L’invariante di rappresentazione 4 l’invariante di rappresentazione, insieme alla funzione di astrazione, riflette le scelte relative alla rappresentazione – deve essere inserito nella documentazione della implementazione commento 4 la funzione di astrazione è definita solo per stati concreti che soddisfano l’invariante 18

L’invariante di rappresentazione di Int. Set I(c) = c. els != null e per

L’invariante di rappresentazione di Int. Set I(c) = c. els != null e per ogni intero i, c. els. get(i) è un Integer e per tutti gli interi i, j, tali che 0 <= i < j < c. els. size(), c. els. get(i). int. Value() != c. els. get(j). int. Value() 4 il vettore non deve essere null 4 gli elementi del vettore sono tutti distinti 4 gli elementi del vettore devono essere Integer – assunti soddisfatti in a 19

L’invariante di rappresentazione di Poly public class Poly { private int[] termini; // la

L’invariante di rappresentazione di Poly public class Poly { private int[] termini; // la rappresentazione private int deg; // la rappresentazione // la funzione di astrazione // a(c) = c 0 + c 1*x + c 2*x 2 +. . . tale che // ci = c. termini[i] se 0 <= i < c. termini. size() // = 0 altrimenti // l’invariante di rappresentazione: // I(c) = c. termini != null e // c. termini. length >= 1 e // c. deg = c. termini. length-1 e // c. deg > 0 ==> c. termini[deg] != 0 4 Il grado e’ l’esponente massimo con coefficiente non zero 4 Il grado (deg) deve essere uguale alla lunghezza dell’array -1 4 Il polinomio vuoto e’ rappresentato da un array di un elemento 20

Validita’ dell’invariante 4 Come facciamo a dimostrare formalmente che tutti gli oggetti del nuovo

Validita’ dell’invariante 4 Come facciamo a dimostrare formalmente che tutti gli oggetti del nuovo tipo di dato soddisfano l’invariante? 4 Notiamo che, dato che la rappresentazione e’ invisibile, nuovi oggetti possono essere creati solo coi costruttori e manipolati solo attraverso metodi pubblici della classe 4 Questo suggerisce che per provare la validita’ dell’invariante si puo’ procedere per induzione sul tipo di dato 21

Induzione sui dati 4 induzione sul numero di invocazioni di metodi usati per produrre

Induzione sui dati 4 induzione sul numero di invocazioni di metodi usati per produrre il valore corrente dell’oggetto 4 Caso Base: dimostriamo che l’invariante vale per gli oggetti restituiti dai costruttori 22

Caso induttivo 4 per tutti i metodi produttori o modificatori separatamente facciamo vedere che

Caso induttivo 4 per tutti i metodi produttori o modificatori separatamente facciamo vedere che 4 assumendo che l’invariante valga (IPOTESI INDUTTIVA) • per this • per tutti gli argomenti del tipo • per gli oggetti del tipo ritornati o modificati da altre chiamate di metodi interne 4 allora quando il metodo termina l’invariante vale • per this • per tutti gli argomenti del tipo • per gli oggetti del tipo ritornati 23

Commenti 4 Notate che questo ragionamento induttivo non sarebbe possibile se la rappresentazione fosse

Commenti 4 Notate che questo ragionamento induttivo non sarebbe possibile se la rappresentazione fosse visibile 4 Chi ci garantirebbe che qualcuno non faccia qualche pasticcio nella rappresentazione dall’esterno? 4 Altro vantaggio di questo stile di programmazione, piu’ sicuro, piu’ facile ragionare sulla correttezza 24

Int. Set 4 Caso Base: il costruttore 4 Caso induttivo: per i modificatori insert

Int. Set 4 Caso Base: il costruttore 4 Caso induttivo: per i modificatori insert e remove assumiamo che this soddisfi l’invariante e facciamo vedere che la soiddisfano anche dopo 4 Notate che gli altri metodi non modificatori preservano banalmente l’invariante (non modificano this) 25

Int. Set 1 public class Int. Set { private Vector els; // la rappresentazione

Int. Set 1 public class Int. Set { private Vector els; // la rappresentazione // I(c) = c. els != null e // per ogni intero i, c. els. get(i) è un Integer // e per tutti gli interi i, j, tali che // 0 <= i < j < c. els. size(), // c. els. get(i). int. Value() != // c. els. get(j). int. Value() public Int. Set () {els = new Vector(); } 4 il costruttore soddisfa l’invariante perché restituisce un Vector vuoto (non null) 26

Int. Set 2 // I(c) = c. els != null e // per ogni

Int. Set 2 // I(c) = c. els != null e // per ogni intero i, c. els. get(i) è un Integer // e per tutti gli interi i, j, tali che // 0 <= i < j < c. els. size(), // c. els. get(i). int. Value() != // c. els. get(j). int. Value() public void insert (int x) {Integer y = new Integer(x); if (get. Index(y) < 0) els. add(y); } private int get. Index (Integer x) // EFFECTS: se x occorre in this ritorna la // posizione in cui si trova, altrimenti -1 4 il metodo insert preserva l’invariante perché aggiunge x a this solo se x non è già in this, e lo aggiunge come Integer 27

Int. Set 3 // I(c) = c. els != null e // per ogni

Int. Set 3 // I(c) = c. els != null e // per ogni intero i, c. els. get(i) è un Integer // e per tutti gli interi i, j, tali che // 0 <= i < j < c. els. size(), // c. els. get(i). int. Value() != // c. els. get(j). int. Value() public void remove (int x) {int i = get. Index(new Integer(x)); if (i < 0) return; els. set(i, els. last. Element()); els. remove(els. size() - 1); } 4 il metodo remove preserva l’invariante perché rimuove solo 28

Correttezza 4 dopo avere dimostrato l’invariante possiamo provare la correttezza dei metodi 4 per

Correttezza 4 dopo avere dimostrato l’invariante possiamo provare la correttezza dei metodi 4 per ogni metodo separatamente, facciamo vedere che l’implementazione soddisfa la specifica – usando il fatto che gli oggetti concreti soddisfano l’invariante – usando la funzione di astrazione per collegare dati concreti ed astratti – assumendo che le chiamate di altri metodi della classe interne siano corrette 29

Correttezza di Int. Set 1 public class Int. Set { private Vector els; //

Correttezza di Int. Set 1 public class Int. Set { private Vector els; // la rappresentazione // la funzione di astrazione // a(c) = { c. els. get(i). int. Value() | 0 <= i < c. els. size() } public Int. Set () // EFFECTS: inizializza this a vuoto {els = new Vector(); } 4 l’astrazione di un vettore vuoto è proprio l’insieme vuoto 30

Correttezza di Int. Set 2 public class Int. Set { private Vector els; //

Correttezza di Int. Set 2 public class Int. Set { private Vector els; // la rappresentazione // la funzione di astrazione // a(c) = { c. els. get(i). int. Value() | 0 <= i < c. els. size() } public int size () // EFFECTS: ritorna la cardinalità di this {return els. size(); } 4 il numero di elementi del vettore è la cardinalità dell’insieme perché – la funzione di astrazione mappa gli elementi del vettore in quelli dell’insieme – il rep invariant garantisce che non ci sono elementi duplicati in els • Possiamo usare size() di Vector 31

Correttezza di Int. Set 3 public void remove (int x) // MODIFIES: this //

Correttezza di Int. Set 3 public void remove (int x) // MODIFIES: this // EFFECTS: toglie x da this {int i = get. Index(new Integer(x)); if (i < 0) return; els. set(i, els. last. Element()); els. remove(els. size() - 1); } 4 se x non occorre nel vettore non fa niente – corretto perché in base alla funzione di astrazione x non appartiene all’insieme 4 se x occorre nel vettore lo rimuove – e quindi in base alla funzione di astrazione x non appartiene all’insieme modificato 32

Correttezza di Int. Set 4 public void remove (int x) // MODIFIES: this //

Correttezza di Int. Set 4 public void remove (int x) // MODIFIES: this // EFFECTS: toglie x da this {int i = get. Index(new Integer(x)); if (i < 0) return; els. remove(i); } 4 Il rep invariant garantisce che non ci sono elementi duplicati (get. Index ritorna l’unico indice in cui occorre l’elemento se compare) 33

Eccezioni 4 L’invariante garantisce anche il vettore non sia null 4 Di conseguenza nei

Eccezioni 4 L’invariante garantisce anche il vettore non sia null 4 Di conseguenza nei metodi, quando si accede al vettore, non viene sollevata una eccezione (che sarebbe sbagliata, perche’ non prevista nella specifica) 34

Come scegliere l’invariante? 4 L’invariante riflette le scelte fatte nella rappresentazione 4 Deve valere

Come scegliere l’invariante? 4 L’invariante riflette le scelte fatte nella rappresentazione 4 Deve valere per tutti gli oggetti del tipo (ragionando in modo induttivo) 4 Deve contenere abbastanza informazione per derivare le proprieta’ richieste nella specifica, sia dei dati che dei metodi 4 Per esempio: per la correttezza di remove e size e’ necessario sapere che nel vettore non ci sono occorrenze multiple (queste condizioni sono esattamente catturate dal rep invariant) 35

Invariante e f di astrazione 4 Devono sempre essere inserite commento all’implementazione 4 Sono

Invariante e f di astrazione 4 Devono sempre essere inserite commento all’implementazione 4 Sono fondamentali per ragionare sulla correttezza dell’implementazione del tipo di dato astratto 4 Possono anche essere implementate ed utilizzate da programmi test per verificare le proprieta’ a run-time 36

Funzione di astrazione 4 non avendo una rappresentazione esplicita dei valori astratti, possiamo rappresentarli

Funzione di astrazione 4 non avendo una rappresentazione esplicita dei valori astratti, possiamo rappresentarli come stringhe 4 a questo punto, possiamo implementare la funzione di astrazione, che è esattamente il metodo to. String – utile per stampare valori astratti // a(c) = { c. els. get(i). int. Value() | 0 <= i < c. els. size() } 37

to. String per Int. Set // a(c) = { c. els. get(i). int. Value()

to. String per Int. Set // a(c) = { c. els. get(i). int. Value() | 0 <= i < c. els. size() } public String to. String () {String s = "{"; for (int i = 0; i < els. size() - 1; i++) { s = s + els. get(i). to. String() + ", "; } if (els. size() > 0) { s = s + els. get(els. size() - 1). to. String(); } s = s + "}"; return (s); } 38

Invariante di rappresentazione 4 Usiamo un metodo rep. Ok che verifica l’invariante pubblico perché

Invariante di rappresentazione 4 Usiamo un metodo rep. Ok che verifica l’invariante pubblico perché deve poter essere chiamato da fuori della sua classe 4 ha sempre la seguente specifica public boolean rep 0 k () // EFFECTS: ritorna true se il rep invariant // vale per this, altrimenti ritorna false 39

rep. OK 4 può essere usato da programmi di test per verificare se una

rep. OK 4 può essere usato da programmi di test per verificare se una implementazione preserva l’invariante 4 può essere usato dentro le implementazioni di costruttori e metodi – creatori, modificatori e produttori dovrebbero chiamarlo prima di ritornare per assicurarsi che per l’oggetto costruito o modificato vale l’invariante • per esempio, dovrebbero chiamarlo insert e remove di Int. Set, add, mul, minus di Poly 4 se l’invariante non vale si dovrebbe sollevare Failure. Exception 40

rep. OK per Int. Set public class Int. Set { private Vector els; //

rep. OK per Int. Set public class Int. Set { private Vector els; // la rappresentazione // I(c) = c. els != null e // per ogni intero i, c. els. get(i) è un Integer // e per tutti gli interi i, j, tali che // 0 <= i < j < c. els. size(), // c. els. get(i). int. Value() != // c. els. get(j). int. Value() 41

rep. OK per Int. Set public boolean rep. Ok() { if (els == null)

rep. OK per Int. Set public boolean rep. Ok() { if (els == null) return false; for (int i = 0; i < els. size(); i++) { Object x = els. get(i); if (! (x instanceof Integer)) return false; for (int j = i + 1; j < els. size(); j++) if (x. equals (els. get(j))) return false; } return true; } 42