1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

52
1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti

Transcript of 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

Page 1: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

1

Astrazioni sui dati :Ragionare sui Tipi di Dato Astratti

Page 2: 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

Page 3: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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)

Page 4: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 5: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 6: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 7: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 8: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 9: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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}

Page 10: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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}

Page 11: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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.

Page 12: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 13: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 14: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 15: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 16: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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)

Page 17: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 18: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 19: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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())

Page 20: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 21: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 22: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 23: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 24: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 25: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 26: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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)

Page 27: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 28: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 29: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 30: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 31: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 32: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 33: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato 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

Page 34: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 35: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 36: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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)

Page 37: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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)

Page 38: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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)

Page 39: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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;

Page 40: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 41: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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)

Page 42: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 43: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 44: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 45: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

45

Per i metodi

Bisogna ragionare sulle specifiche, uno ad uno usando invariante e funzione di astrazione

Page 46: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 47: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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() }

Page 48: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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);}

Page 49: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 50: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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

Page 51: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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()

Page 52: 1 Astrazioni sui dati : Ragionare sui Tipi di Dato Astratti.

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; }