LIP: 19 Aprile 2005
Contenuto Soluzione Compitino Tipo di dato MultiSet, estensione con sottoclasse
Primo Esercizio
Facile public class PairInt { private int left; private int right; public PairInt (int x,int y) { left=x;right=y; } public int left() { return left; } public int right() { return right;} public void incr(int x,int y) { left=left+ x; right=right+ y; }
Tester di PairInt public class Tester{ public static void main(String[] args) { PairInt p1=new PairInt(2,4); PairInt p2=new PairInt(6,9); System.out.println(p1.left()); System.out.println(p1.right()); p1.incr(p2.left(),p2.right()); System.out.println(p1.left()); System.out.println(p1.right()); }
Secondo Esercizio Si implementi (con una rappresentazione privata) la seguente specifica del tipo di dato PairQueue Coda (mantiene gli elementi in ordine FIFO) Gli elementi sono coppie di interi (PairInt) Ex: (3,5), (9,0),(4,6) (3,5) primo inserito (primo della coda) (4,6) ultimo inserito
public class PairQueue { // OVERVIEW: un PairQueue è una coda di elementi, che sono coppie di interi (tipo PairInt); gli elementi //sono ordinati in base allordine di inserimento secondo una politica first-in-first-out. è modificabile. // costruttore public PairQueue () { // EFFECTS: costruisce un nuovo PairQueue vuoto.} //metodi public void dequeue () throws EmptyException { //MODIFIES:this // EFFECTS: se this e vuoto solleva EmptyException, altrimenti rimuove il primo elemento di this.} public PairInt first () throws EmptyException { // EFFECTS: se this e vuoto solleva EmptyException, altrimenti restituisce il primo elemento di this.} public void enqueue (PairInt p) throws NullPointerException { //MODIFIES:this // EFFECTS: se p e null solleva NullPointerException, altrimenti //inserisce p in this.} public String toString () { // EFFECTS: restituisce una stringa che rappresenta il contenuto di this, ovvero la sequenza ordinata delle coppie.}
Implementazione Facile: usando un Vector Simile alla pila, basta inserire da una parte e rimuovere dallaltra Bisogna definire anche la classe che definisce leccezione EmptyException
Eccezione Controllata public class EmptyException extends Exception{ public EmptyException(String s) {super(s);} }
public class PairQueue{ private Vector coda; public PairQueue () { coda=new Vector(); } public void dequeue () throws EmptyException { if (coda.size()==0) throw new EmptyException("PairQueue.first"); coda.removeElementAt(0); } public PairInt first () throws EmptyException { if (coda.size()==0) throw new EmptyException("PairQueue.first"); return (PairInt) coda.elementAt(0); } public void enqueue (PairInt p) throws NullPointerException { coda.addElement(p); } public String toString () { String s=""; for (int j=0; j< coda.size();j++){ PairInt x=(PairInt) coda.elementAt(j); s=s + "(" + x.left() + "," + x.right() + ")"; } return s; }
Tester Si puo procedere in modo incrementale estendendo quello di prima Provare i metodi che inseriscono e rimuovono toString fondamentale per testing (dato che la rappresentazione e privata non avrei modo altrimenti di guardare il contenuto della coda) Bisogna testare anche il caso vuoto per vedere che i metodi lancino leccezione
public class Tester{ public static void main(String[] args) throws EmptyException { PairInt p1=new PairInt(2,4); PairInt p2=new PairInt(6,9); System.out.println(p1.left()); System.out.println(p1.right()); p1.incr(p2.left(),p2.right()); System.out.println(p1.left()); System.out.println(p1.right()); PairQueue q=new PairQueue(); q.enqueue(p2); q.enqueue(p1); System.out.println(q.toString()); PairInt p3=q.first(); System.out.println(p3.left()); System.out.println(p3.right()); q.dequeue(); q.dequeue(); // e vuota: il main solleva leccezione? try{q.first();} //altrimenti tutto termina sollevando leccezione (deve comunque essere catch (EmptyException e) // riportata nel throws per compilarlo (controllata) {System.out.println("VUOTA");} }
Terzo Esercizio Definire una classe nuova che contiene una procedura statica per modificare PairQueue public class PairQueueProc { // metodi statici public static PairQueue append (Pairqueue p,PairQueue q) throws NullPointerException { // EFFECTS: se p o q sono null solleva NullPointerException, altrimenti restituisce un nuovo PairQueue che e la concatenazione di p e q in cui tutte le coppie sono state //incrementate di 1. Esempio: dati p=(3,6),(4,8) e q=(7,9), restituisce s=(4,7),(5,9),(8,10). } }
Problema Va scritta usando la specifica di PairQueue (anche se la rappresentazione ce non puo accedervi perche e privata) Non deve modificare le code p e q passate per parametro dato che nella specifica di append non e riportato (non ce MODIFIES) Se le modificasse limplementazione violerebbe la specifica Problema grave: se limplementazione non soddisfa la specifica, la specifica sarebbe inutile (chi usa append dovrebbe andare a vedere limplementazione per sapere cosa fa (se le modifica o no) Si perderebbe il meccanismo di astrazione tramite specifica che e la base della programmazione modulare
public static PairQueue append (PairQueue p,PairQueue q) throws NullPointerException { if (p==null || q==null) throw new NullPointerException("append"); PairQueue s=new PairQueue(); PairQueue p1=new PairQueue(); \\ per memorizzare p PairQueue q1=new PairQueue(); \\ per memorizzare q try{ while(true){ PairInt x = p.first(); p1.enqueue(new PairInt(x.left(),x.right())); x.incr(1,1); s.enqueue(x); p.dequeue();} } catch (EmptyException e1) {p=p1;System.out.println("termina1");} \\ ripristino p try{ while(true){ PairInt y = q.first(); q1.enqueue(new PairInt(y.left(),y.right())); y.incr(1,1); s.enqueue(y); q.dequeue();} } catch (EmptyException e2) {System.out.println("termina2"); q=q1; } \\ ripristino q return s; }
Nuovo Esercizio Variante di IntSet (visto a lezione di MP) Definiamo MIntSet Sono multiinsiemi di interi Insieme: collezione di elementi, non esiste il concetto di numero di occorrenze (Ex. {1,7,8}) Multiinsieme: collezione di elementi, che possono occorrere zero o piu volte (Ex. {1,7,8,7,1,1}).
Operazioni su Multiset Le operazioni sono analoghe a quelle degli insiemi solo che aggiungono e rimuovono una occorrenza Analogamente per unione ed intersezione (quando unisco Faccio la somma del numero di occorrenze, quando interseco prendo per ogni elemento solo il numero di occorrenze che sta in entrambi) Vediamo la specifica
public class IMultiset { //OVERVIEW: un IMultiset e' un multiinsieme di interi ( in //cui ogni elemento puo' occorrere piu' volte), es. {1,5,4,1,4}. //E' modficabile //metodi e costruttori public IMultiset () { //EFFECTS: inizializza this al multiinsieme vuoto} public int isin(int x) { //EFFECTS: restituisce il numero di occorrenze di x (0 se non compare)} public void insert(int x){ //EFFECTS: inserisce unoccorrenza di x in this //MODIFIES: this} public void remove(int x){ //EFFECTS: rimuove una occorrenza di x da this (se non ce non fa nulla) //MODIFIES: this }
public String toString(){ //EFFECTS: restituisce una stringa che contiene linsieme this} public void union(IMultiset x){ //EFFECTS: modifica this, facendo this unione x //MODIFIES: this} public void inter(IMultiset x){ //EFFECTS: modifica this, facendo this intersecato x //MODIFIES: this} }
Implementazione La scelta fondamentale e quella della rappresentazione Deve permettere di implementare i metodi in modo efficiente Come si puo rappresentare? Analogo ad Intset con un Vector Differenza: occorrenze multiple di elementi
Attenzione Metodi insert e remove tolgono e aggiungono una occorrenza Metodo IsIn deve contare il numero delle occorrenze Metodi union e inter si puo sfruttare IsIn (piu efficiente)
Estensione Vogliamo estendere il tipo di dato IMultiSet (a posteriori) aggiungendo un operazione (metodo distanza maxmul), che restituisce la molteplicita massima presente nellinsieme) Molteplicita: numero occorrenze Vogliamo una sottoclasse che aggiunge il metodo
Problema La rappresentazione di Imultiset (comunque sia fatta) deve essere private Necessario per realizzare lastrazione: mascherare limplementazione Le sottoclassi (e analogamente tutti gli altri moduli) pero in questo modo non possono accedere alla rappresentazione Come si fa a fare maxmul???????
Rappresentazione Protected Se mettiamo la rappresentazione protected (invece che private) le sottoclassi possono accedere (non tutte le altre classi) Si perde lastrazione verso le sottoclassi (il loro codice dipende dallimplementazione della superclasse) Si mantiene lastrazione verso tutte le altre classi (il loro codice e per forza indipendente dallimplementazione della superclasse) Buon compromesso (rispetto ad usare public), ma vedremo come si puo fare meglio usando gli iteratori.)
Specifica della sottoclasse public class Imultisetext extends IMultiset { //OVERVIEW: aggiunge un metodo //metodi e costruttori public Imultisetext() { //EFFECTS: inizializza this al multiinsieme vuoto} public int maxmul() { //EFFECTS: restituisce il massimo numero di occorrenze tra tutti gli elementi di this (0 se e vuoto)}
Soluzione Per avere una implementazione piu efficiente usiamo una rappresentazione particolare (invece di inserire gli elementi in modo casuale nel Vector) Usiamo un Vettore di coppie del tipo (elemento,n. occorrenze) Serve tipo ausiliario PairInt
Idea della implementazione Tutte le informazioni su un certo elemento sono raggruppate insieme Esempio (7,3),(3,1),(1,3) rappresenta il multiinsieme {7,7,7,3,1,1,1} Nota che non vogliamo coppie diverse per lo stesso elemento tipo (1,2),(1,3). Una sola coppia deve descrivere lelemento 1, tipo (1,5) Questa rappresentazione permette una implementazione piu efficiente dei metodi soprattutto quelli della sottoclasse
public class PairInt { private int left; private int right; public PairInt (int x,int y) { left=x;right=y; } public int left() { return left; } public int right() { return right;} }
public class IMultiset{ protected Vector els; public IMultiset () { els=new Vector();} public int isin(int x) { for (int i=0; i<els.size(); i++) { PairInt q=(PairInt) els.elementAt(i); if (x==q.left()) { return q.right();}} return 0;} public void insert(int x){ if (isin(x)==0) { els.addElement(new PairInt(x,1)); return;} for (int i=0; i<els.size(); i++) { PairInt q=(PairInt) els.elementAt(i); if (x==q.left()) { int y=q.right(); y++; PairInt p=new PairInt(x,y); els.setElementAt(p,i); } } }
public void remove(int x){ if (isin(x)==0) return; for (int i=0; i<els.size(); i++) { PairInt p=(PairInt) els.elementAt(i); if (x==p.left()) { int y= p.right(); y--; if (y>0) els.setElementAt(new PairInt(x,y),i); else els.removeElementAt(i); } } } public String toString () { String s="{" ; for (int j=0; j< els.size();j++){ PairInt x=(PairInt) els.elementAt(j); for (int i=0; i< x.right();i++){ s=s + x.left() + ",";} } s =s + "}"; return s; }
public class ExtMul extends IMultiset{ public ExtMul () { } public int maxmul() { int max=0; for (int j=0; j< els.size();j++){ PairInt x=(PairInt) els.elementAt(j); if (x.right() > max) {max=x.right();} } return max; }
public void inter(IMultiset x){ for (int i=0; i<els.size(); i++) { PairInt p=(PairInt) els.elementAt(i); int num=x.isin(p.left()); if (num ==0) {els.removeElementAt(i);} else {if (num < p.right()) {els.setElementAt(new PairInt(p.left(),num),i);} }}} public void union ( IMultiset x){ for (int i=0; i<x.els.size(); i++) { PairInt p=(PairInt) x.els.elementAt(i); if (isin(p.left()) ==0) {els.addElement(p);} else {for (int j=0; j<els.size(); j++) { PairInt q=(PairInt) els.elementAt(j); if (q.left()==p.left()) { els.setElementAt(new PairInt(p.left(),q.right()+p.right()),i);} } }}} }