1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.
-
Upload
nerezza-lisa -
Category
Documents
-
view
222 -
download
1
Transcript of 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.
1
Astrazioni sui dati :Ragionare sui Tipi di Dato Astratti
2
Correttezza dell’implementazione L’implementazione del tipo di dato soddisfa la
specifica Rispetto al caso delle procedure stand-alone piu’
complicato
1. Specifica: descrizione astratta degli oggetti + metodi pubblici e le loro proprieta’
2. Implementazione: rappresentazione + implementazione dei metodi
3
Correttezza dell’implementazioneBisogna 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)
4
Correttezza dell’implementazioneLe due cose sono pero’ strettamente correlate1. 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
5
Servono altre informazioni
Funzione di astrazione: collega la rappresentazione degli oggetti con la loro descrizione astratta
Invariante di rappresentazione: specifica proprieta’ della rappresentazione degli oggetti
6
Primo problema
se vogliamo dimostrare che le implementazioni dei metodi soddisfano le rispettive specifiche
l’implementazione utilizza la rappresentazione– nel caso di IntSet– private Vector els;
le specifiche esprimono proprietà dei dati astratti, ovvero degli insiemi– nel caso di IntSet
public boolean isIn (int x) // EFFECTS: se x appartiene a this ritorna
// true, altrimenti false
Per sapere se l’implementazione la soddisfa devo sapere come è rappresentato un insieme
7
Cosa manca
Un modo per mettere in relazione tra loro i due “insiemi di valori” concreti ed astratti
valori concreti: stati degli oggetti della classe (espressi da un insieme di var d’istanza)
valori astratti: il tipo di dato specificato nel caso di IntSet: devo collegare il vettore els
all’insieme di interi che rappresenta
8
La funzione di astrazione 1
la funzione di astrazione cattura l’intenzione del progettista nello scegliere una particolare rappresentazione
la funzione di astrazione
: C --> A
porta da uno stato concreto– lo stato di un oggetto della classe C
a uno stato astratto – lo stato dell’oggetto astratto
9
Esempio
public class IntSet { // OVERVIEW: un IntSet è un insieme modificabile // di interi di dimensione qualunque private Vector els; // la rappresentazione
deve mappare vettori di interi in insiemi ([1,2]) = {1,2} ([3,1]) = {1,3} ([1,3,2]) = {1,2,3}
10
La funzione di astrazione 2
la funzione di astrazione è generalmente una funzione molti-a-uno
public class IntSet { // OVERVIEW: un IntSet è 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 )
– ([1,2]) = {1,2}– ([2,1]) = {1,2}
11
Come definire la funzione di astrazione?
il problema è che non abbiamo una rappresentazione esplicita dei valori astratti (solo specifica informale)
l’oggetto da rappresentare può essere descritto da un oggetto astratto tipico della classe dato nella OVERVIEW della specifica.
per dare la funzione possiamo usare la notazione matematica e la notazione del linguaggio di programmazione con opportune abbreviazioni.
12
La funzione di astrazione di IntSet
public class IntSet { // OVERVIEW: un IntSet è un insieme modificabile // di interi di dimensione qualunque // un tipico IntSet è {x1, ,xn } private Vector els; // la rappresentazione
(c) = { c.els.get(i).intValue() | 0 <= i < c.els.size() }
C metavariabile che rappresenta un oggetto generico di IntSet
13
La funzione di astrazione di Poly public class Poly { // OVERVIEW: un Poly è un polinomio a // cofficienti interi non modificabile // un tipico Poly: c0 + c1*x + c2*x2 + ...
private int[] termini; // la rappresentazione private int deg; // la rappresentazione
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
La funzione di astrazione collega l’array al polinomio
14
La funzione di astrazione di Polypublic class Poly { // OVERVIEW: un Poly è un polinomio a // cofficienti interi non modificabile // un tipico Poly: c0+ c1 x + c2 x2 + ... private int[] termini; // la rappresentazione private int deg; // la rappresentazione
(c) = c0+ c1 x + c2 x2 + ... tale che
ci = c.termini[i] se 0 <= i < c.termini.length = 0 altrimenti 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
15
Un altro problema
non tutti gli stati concreti “rappresentano” correttamente uno stato astratto
infatti gli oggetti concreti soddisfano delle proprieta’, sono costruiti in modo opportuno
queste proprieta’ servono tipicamente sia per la correttezza della rappresentazione sia per quella dei metodi
Da cosa derivarle
16
Esempio
public class IntSet { // OVERVIEW: un IntSet è un insieme modificabile // di interi di dimensione qualunque // un tipico IntSet è {x1, ,xn } private Vector els; // la rappresentazione // la funzione di astrazione // (c) = { c.els.get(i).intValue() | 0 <= i < c.els.size() } 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)
17
L’invariante di rappresentazione
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
esprime le proprieta’ fondamentali della rappresentazione (implementazione) degli oggetti in base alle quali abbiamo definito i metodi
18
L’invariante di rappresentazione
l’invariante di rappresentazione, insieme alla funzione di astrazione, riflette le scelte relative alla rappresentazione– deve essere inserito nella documentazione della implementazione
come commento
la funzione di astrazione è definita solo per stati concreti che soddisfano l’invariante
19
L’invariante di rappresentazione di IntSet
// 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).intValue() != // c.els.get(j).intValue()
il vettore non deve essere null gli elementi del vettore sono tutti distinti gli elementi del vettore devono essere Integer
– assunti soddisfatti in Al solito usiamo i metodi per esprimere le
proprieta’ (vedi intValue())
20
L’invariante di rappresentazione di Poly public class Poly { private int[] termini; // la rappresentazione private int deg; // la rappresentazione
// la funzione di astrazione // (c) = c0 + c1*x + c2*x2 + ... 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
Il grado e’ l’esponente massimo con coefficiente non zero Il grado (deg) deve essere uguale alla lunghezza dell’array -1 Il polinomio vuoto e’ rappresentato da un array di un elemento
21
Validita’ dell’invariante
Come facciamo a dimostrare formalmente che tutti gli oggetti del nuovo tipo di dato soddisfano l’invariante?
Notiamo che, dato che la rappresentazione e’ invisibile, nuovi oggetti possono essere creati solo coi costruttori e manipolati solo attraverso metodi pubblici della classe
Questo suggerisce che per provare la validita’ dell’invariante si puo’ procedere per induzione sul tipo di dato
22
Induzione sui dati
induzione sul numero di invocazioni di metodi usati per produrre il valore corrente dell’oggetto
Caso Base: dimostriamo che l’invariante vale per gli oggetti restituiti dai costruttori
23
Caso induttivo
per tutti i metodi produttori o modificatori separatamente facciamo vedere che
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
allora quando il metodo termina l’invariante vale • per this• per tutti gli argomenti del tipo• per gli oggetti del tipo ritornati
24
Commenti
Notate che questo ragionamento induttivo non sarebbe possibile se la rappresentazione fosse visibile
Chi ci garantirebbe che qualcuno non faccia qualche pasticcio nella rappresentazione dall’esterno?
Altro vantaggio di questo stile di programmazione, piu’ sicuro, piu’ facile ragionare sulla correttezza
25
Caso Base: il costruttore Caso induttivo: per i modificatori insert e
remove assumiamo che this soddisfi l’invariante e facciamo vedere che la soiddisfano anche dopo
Notate che gli altri metodi non modificatori preservano banalmente l’invariante (non modificano this)
IntSet
26
IntSet 1
public class IntSet { 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).intValue() != // c.els.get(j).intValue()
public IntSet () {els = new Vector();}
il costruttore soddisfa l’invariante perché restituisce un Vector vuoto (non null)
27
IntSet 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).intValue() != // c.els.get(j).intValue() public void insert (int x) {Integer y = new Integer(x); if (getIndex(y) < 0) els.add(y); } private int getIndex (Integer x)
// EFFECTS: se x occorre in this ritorna la // posizione in cui si trova, altrimenti -1 il metodo insert preserva l’invariante
perché aggiunge x a this solo se x non è già in this, e lo aggiunge come Integer
28
IntSet 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).intValue() != // c.els.get(j).intValue()public void remove (int x) {int i = getIndex(new Integer(x)); if (i < 0) return; els.set(i, els.lastElement()); els.remove(els.size() - 1);}
il metodo remove preserva l’invariante perché rimuove solo
29
Correttezza di Poly 1public class Poly { private int[] termini; // la rappresentazione private int deg; // la 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] != 0public Poly () {termini = new int[1]; deg = 0; }
il primo costruttore soddisfa l’invariante perché restituisce un Array di un elemento e deg = 0
30
Correttezza di Poly 2public class Poly { private int[] termini; // la rappresentazione private int deg; // la 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] != 0public Poly (int c, int n) throws NegativeExponentExc if (n < 0) throw new NegativeExponentExc
(“Poly(int,int) constructor”); if (c == 0) {termini = new int[1]; deg = 0; return; } termini = new int[n+1]; for (int i = 0; i < n; i++) termini[i] = 0; termini[n] = c; deg = n; }
il secondo costruttore soddisfa l’invariante perché testa esplicitamente il caso c=0
31
Correttezza di Poly 3public class Poly { private int[] termini; // la rappresentazione private int deg; // la 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] != 0public Poly sub (Poly q) throws NullPointerException {return add(q.minus()); }
il metodo produttore sub soddisfa l’invariante perché (per ipotesi induttiva)– lo soddisfano q e this – lo soddisfano add e minus
32
Correttezza
dopo avere dimostrato l’invariante dobbiamo provare la correttezza dei metodi
per ogni metodo separatamente, l’implementazione soddisfa la specifica
(assumendo che le chiamate di altri metodi della classe interne siano corrette)– usando il fatto che gli oggetti concreti soddisfano
l’invariante – usando la funzione di astrazione per collegare dati
concreti ed astratti
33
Correttezza di IntSet 1
public class IntSet { private Vector els; // la rappresentazione // la funzione di astrazione // (c) = { c.els.get(i).intValue() | 0 <= i < c.els.size() }public IntSet ()
// EFFECTS: inizializza this a vuoto {els = new Vector();}
l’astrazione di un vettore vuoto è proprio l’insieme vuoto
34
Correttezza di IntSet 2
public class IntSet { private Vector els; // la rappresentazione // la funzione di astrazione // (c) = { c.els.get(i).intValue() | 0 <= i < c.els.size() }public int size () // EFFECTS: ritorna la cardinalità di this {return els.size(); } 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
35
Correttezza di IntSet 3
public void remove (int x) // MODIFIES: this// EFFECTS: toglie x da this
{int i = getIndex(new Integer(x)); if (i < 0) return; els.set(i, els.lastElement()); els.remove(els.size() - 1);}
se x non occorre nel vettore non fa niente– corretto perché in base alla funzione di astrazione x
non appartiene all’insieme se x occorre nel vettore lo rimuove
– e quindi in base alla funzione di astrazione x non appartiene all’insieme modificato
36
Correttezza di IntSet 4public void remove (int x)
// MODIFIES: this// EFFECTS: toglie x da this
{int i = getIndex(new Integer(x)); if (i < 0) return; els.remove(i);}
Il rep invariant garantisce che non ci sono elementi duplicati (getIndex ritorna l’unico indice in cui occorre l’elemento se compare)
37
Eccezioni
L’invariante garantisce anche che il vettore non sia null
Di conseguenza nei metodi, quando si accede al vettore, non viene sollevata una eccezione (che sarebbe sbagliata, perche’ non prevista nella specifica)
38
Come scegliere l’invariante?
L’invariante riflette le scelte fatte nella rappresentazione Deve valere per tutti gli oggetti del tipo (ragionando in
modo induttivo) Deve contenere abbastanza informazione per derivare le
proprieta’ richieste nella specifica, sia dei dati che dei metodi 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)
39
Esempio: l’ambiente dei metodi
public class MethodEnv {
// OVERVIEW: UN METHODENV È UNA
// FUNZIONE PARZIALE DA IDENTIFICATORI
// DI METODI (STRINGHE) A DESCRIZIONI DI
// METODI. NON È MODIFICABILE.
private PairIdeVal[] associazioni;
40
Rappresentazione
•Array di coppie,
PairIdeVal e’ un tipo record che modella
le coppie identificatore (String) e descrizione
di metodo (Mdescr)
•Due variabili nome e valore (Object)
PairIdeVal
41
Funzione di astrazione
(c) = f:String -----> Mdescr
dove 0 <= i < c.associazioni.length() f(c.associazioni[i].nome)= c.associazioni[i].valore
ed f e’ indefinita per tutti gli altri valori (funzione parziale)
•Mappa l’insieme di coppie, contenuto nell’array, nella corrispondente funzione (relazione-------->funzione)
42
Invariante di rappresentazione
•Esprime le proprieta’ della rappresentazione•L’insieme di coppie contenuto nell’array deve rappresentareuna funzione (non tutte le relazioni corrispondono a funzioni)
I(c) = c.associazioni!= null e
per ogni intero i, 0 <= i < c. associazioni.length(),c.associazioni[i].valore è un Mdescr
e per tutti gli interi i,j, tali che 0 <= i < j < c. associazioni.length(),c.associazioni[i].nome != c.associazioni[j].nome
43
Validita’ dell’invariante
L’invariante e’ soddisfatta
(per induzione sui dati, un metodo alla volta)Metodi Produttori (bind ed instantiate):
attenzione se esiste gia’ la descrizione di un
metodo con quel nome non possiamo riinserirlo
44
Commenti
L’invariante esprime una proprieta’ necessaria per la correttezza della rappresentazione dei dati e dei metodi,
rispetto alle loro specifiche Ogni dato concreto che soddisfa l’invariante e’ mappato
tramite la funzione di astrazione in un ambiente dei metodi (come richiesto dall’OVERVIEW)
La funzione di astrazione non ritorna una funzione se applicata ad un oggetto concreto che non soddisfa l’invariante
45
Per i metodi
Bisogna ragionare sulle specifiche, uno ad uno usando invariante e funzione di astrazione
46
Invariante e f di astrazione
Devono sempre essere inserite come commento all’implementazione
Sono fondamentali per ragionare sulla correttezza dell’implementazione del tipo di dato astratto
Possono anche essere implementate ed utilizzate da programmi test per verificare le proprieta’ a run-time
47
Funzione di astrazione
non avendo una rappresentazione esplicita dei valori astratti, possiamo rappresentarli come stringhe
a questo punto, possiamo implementare la funzione di astrazione, che è esattamente il metodo toString– utile per stampare valori astratti
// (c) = { c.els.get(i).intValue() | 0 <= i < c.els.size() }
48
toString per IntSet
// (c) = { c.els.get(i).intValue() | 0 <= i < c.els.size() }
public String toString ()
{String s = "{";
for (int i = 0; i < els.size() - 1; i++) {
s = s + els.get(i).toString() + ","; }
if (els.size() > 0) {
s = s + els.get(els.size() - 1).toString(); }
s = s + "}";
return (s);}
49
Invariante di rappresentazione
Usiamo un metodo repOk che verifica l’invariante pubblico perché deve poter essere chiamato da fuori della sua classe
ha sempre la seguente specifica
public boolean rep0k ()
// EFFECTS: ritorna true se il rep invariant
// vale per this, altrimenti ritorna false
50
repOK
può essere usato da programmi di test per verificare se una implementazione preserva l’invariante
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 IntSet, add, mul, minus di Poly
se l’invariante non vale si dovrebbe sollevare FailureException
51
repOK per IntSet
public class IntSet { 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).intValue() != // c.els.get(j).intValue()
52
repOK per IntSet
public boolean repOk() {
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; }