Tipi di dati astratti ADT 1 Dati strutturati e algoritmi Una struttura di dati è un insieme di regole per organizzare e manipolare dei dati Un algoritmo è un procedimento composto in passi elementari in grado di risolvere un determinato problema in un numero finito di passi. I passi elementari devono essere ben definiti ADT 2 1
Tipi di dati astratti Un tipo di dato astratto (in inglese ADT per Abstract Data Type) è un modello matematico di una struttura di dati che definisce l insieme dei valori che i dati possono assumere, le operazioni che si possono eseguire sui dati e i parametri ad esse applicabili Nel definire un ADT si specificano le operazioni senza specificare come queste verranno poi realizzate, questo corrisponde alla definizione di una interfaccia cioè di un elenco delle possibili operazioni ADT 3 Stack Abbiamo già visto le proprietà di uno stack cioè di un contenitore di oggetti che possono essere inseriti e tolti con una politica di tipo LIFO (last in first out). Il primo oggetto ad uscire dal contenitore sarà sempre quello che è stato inserito per ultimo ADT 4 2
Stack come ADT Uno stack può essere manipolato con due metodi push(o): inserisce nello stack l oggetto o Ingresso: l oggetto Uscita: nessuna pop(): toglie dallo stack l ultimo elemento inserito e ne restituisce il valore; può produrre un errore se lo stack è vuoto Ingresso: nessuno Uscita: un oggetto metodi addizionali size(): restituisce il numero di oggetti nello stack Ingresso: nessuno Uscita: un intero isempty(): restituisce un booleano vero se lo stack è vuoto Ingresso: nessuno Uscita: un booleano top(): restituisce il valore dell oggetto in allo stack senza modificare lo stack; produce un errore se lo stack è vuoto Ingresso: nessuno Uscita: un oggetto ADT 5 Inserimento di un elemento in uno stack Vettore s 0 primo 1 secondo 2 3 4 5 top 1 terzo Inserzione di terzo Vettore s primo secondo terzo top 2 top indica la posizione della dello stack, il valore iniziale deve essere -1 nell esempio la capienza dello stack è pari a 6 ADT 6 3
Estrazione di due elementi dallo stack primo secondo terzo top 3 secondo terzo estrazione di due oggetti escono: terzo e secondo primo top 0 ADT 7 StackDiStringhe.java gestione di uno stack di stringhe realizzato con un vettore class StackDiStringhe { private static final int CAPIENZA = 6; private String[] s; private int top; public StackDiStringhe () { s = new String[CAPIENZA]; top = -1 public int size () { return top + 1; public boolean isempty () { return top < 0; public void push (String elemento) { if (size() == CAPIENZA) Prims.errore("Overflow di uno stack"); else s[++top] = elemento; public String top () { if (isempty()) Prims.errore("Lo stack e' vuoto"); return s[top]; public String pop () { if (isempty()) Prims.errore("Lo stack e' vuoto"); String elemento = s[top]; s[top--] = ; return elemento; ADT 8 4
Main1.java rovescia l'ingresso in uscita con uno stack di stringhe class Main1 { public static void main (String[] arg) { String str; StackDiStringhe st = new StackDiStringhe(); while ((str = Prims.prendi())!= ) st.push(str); Prims.metti("Si sono lette: "+ st.size() + " righe" + Prims.fineRiga()); Prims.metti( Testa: +st.top()+prims.fineriga()); while (!st.isempty()) Prims.metti(st.pop() + Prims.fineRiga()); Prims.metti("Lo stack contiene " + st.size() + " elementi" + Prims.fineRiga()); st.pop(); // produce un errore ADT 9 Problemi La realizzazione data soffre di alcune limitazioni che possono però essere superate utilizzando le caratteristiche di JAVA: l ADT stack potrebbe essere realizzato in modo diverso, è opportuno svincolare l interfaccia dalla realizzazione per ogni tipo di dato dobbiamo realizzare una nuova classe (StackDiInteri, StackDiCoppie, ecc.) la gestione degli errori è del tutto inadeguata ADT 10 5
interface La dichiarazione di un interfaccia introduce un nuovo tipo di riferimento che ha come membri solo costanti e metodi astratti. Un interfaccia non dispone di una realizzazione ma una o più classi, del tutto indipendenti, possono realizzare l interfaccia Un interfaccia può estendere un altra interfaccia (aggiungendo nuovi metodi e/o mascherando le costanti) Una classe può realizzare una o più interfacce (oltre che estendere una classe) ADT 11 Tipi di dati astratti e interfacce Un interfaccia JAVA si presta molto bene alla definizione di un tipo di dato astratto senza dover definire la sua realizzazione Nel definire l interfaccia non abbiamo bisogno di impegnarci sul tipo degli oggetti che verranno gestiti dall interfaccia: saranno genericamente di tipo Object Possiamo indicare esplicitamente che un metodo può produrre un errore ADT 12 6
exception Quando un programma viola le regole semantiche di JAVA la macchina virtuale JAVA segnala l errore lanciando un eccezione che può essere catturata da un diverso punto del programma; un eccezione può anche essere lanciata esplicitamente Le eccezioni sono esemplari della classe Throwable o di una classe da essa derivata Il segmento di programma che gestisce un eccezione è il blocco catch che segue il blocco try all interno del quale si è verificato l errore ADT 13 Il costrutto try{...catch{... try { // istruzioni che // possono produrre // un errore catch { // gestione // dell eventuale // errore errore gestione dell errore Il verificarsi di un errore lancia un eccezione che viene catturata per la gestione dell anomalia ADT 14 7
Stack.java un esempio di interface public interface Stack { public int size(); public boolean isempty(); public Object top() throws StackEmptyException; public void push (Object element); public Object pop() throws StackEmptyException; ADT 15 Realizzazione di Stack con un vettore class ArrayStack implements Stack { static final int CAPIENZA = 10; private int capienza; private Object[] s; private int top; public ArrayStack (int capienza) { top = -1; this.capienza = capienza; s = new Object[capienza]; public ArrayStack () { this(capienza); public int size () { return top + 1; ADT 16 8
Realizzazione di Stack con un vettore (segue) public boolean isempty () { return top < 0; public void push (Object elemento) { if (size() == capienza) throw new StackFullException("stack overflow"); s[++top] = elemento; public Object top () throws StackEmptyException { if (isempty()) throw new StackEmptyException("stack vuoto"); return s[top]; public Object pop () throws StackEmptyException { if (isempty()) throw new StackEmptyException("stack vuoto"); Object elemento = s[top]; s[top--] = ; return elemento; ADT 17 Main2.java rovescia l'ingresso in uscita con uno stack di stringhe class Main2 { public static void main (String[] arg) { String str; Stack st = new ArrayStack(); while ((str = Prims.prendi())!= ) st.push(str); Prims.metti( N:"+st.size()+"righe"+Prims.fineRiga()); Prims.metti(": "+ st.top() + Prims.fineRiga()); while (!st.isempty()) Prims.metti(st.pop() + Prims.fineRiga()); Prims.metti("contiene " + st.size() + " elementi" + Prims.fineRiga()); try { st.pop(); // produce un errore catch (StackEmptyException e) { Prims.metti("Errore" + Prims.fineRiga()); ADT 18 9
Main3.java rovescia l'ingresso in uscita con uno stack di interi class Main3 { public static void main (String[] arg) { String str; Stack st = new ArrayStack(); while ((str = Prims.prendi())!= ) st.push(new Integer(Prims.intero(str))); Prims.metti("lette: "+st.size()+" righe"+prims.fineriga()); Prims.metti(": " + st.top() + Prims.fineRiga()); while (!st.isempty()) Prims.metti(st.pop() + Prims.fineRiga()); Prims.metti("N el: "+st.size()+prims.fineriga()); try { st.pop(); // produce un errore catch (StackEmptyException e) { Prims.metti("Errore 1" + Prims.fineRiga()); try { Integer i = (Integer)st.top(); // produce un errore catch (StackEmptyException e) { Prims.metti("Errore 2" + Prims.fineRiga()); ADT 19 Gestione degli errori public class StackFullException extends RuntimeException { public StackFullException(String err) { super(err); public class StackEmptyException extends RuntimeException { public StackEmptyException(String err){ super(err); ADT 20 10
Coda - queue La coda è un altra struttura di dati di tipo contenitore nella quale l inserzione e l eliminazione degli elementi avviene con il principio che chi arriva per primo viene servito per primo (FIFO - first in first out) Pensando alla coda come ad una sequenza di oggetti si dice che gli elementi vengono inseriti alla fine o in coda e vengono estratti dalla della coda La coda modella la situazione ad un sportello che fornisce un servizio ADT 21 Tipo di dato astratto coda Una coda può essere manipolata con due metodi: enqueue(o): inserisce l oggetto o alla fine della coda Ingresso: un oggetto Uscita: nessuna dequeue(): estrae dalla coda l elemento di e ne restituisce il valore; produce un errore se la coda è vuota Ingresso: nessuno Uscita: un oggetto metodi addizionali: size(): restituisce il numero di oggetti presenti in coda Ingresso: nessuno Uscita: un intero isempty(): restituisce un booleano vero se la coda è vuota Ingresso: nessuno Uscita: un booleano front(): restituisce il valore dell oggetto in alla coda senza rimuoverlo, produce un errore se la coda è vuota Ingresso: nessuno Uscita: un oggetto ADT 22 11
Operazioni su di una coda coda f e d c b a coda f e d c b a Due dequeue() coda g g f e d c enqueue(g) coda g f e d c Un dequeue() Come si è fatto per il TDA stack anche per le code possiamo definire un interfaccia JAVA ADT 23 interface Queue.java public interface Queue { public int size (); public boolean isempty (); public Object front () throws QueueEmptyException; public void enqueue (Object elemento); public Object dequeue () throws QueueEmptyException; // QueueEmptyException.java public class QueueEmptyException extends RuntimeException { public QueueEmptyException(String err) { super(err); ADT 24 12
Realizzazione di una coda con un array Per realizzare una coda possiamo utilizzare un array Q; tenendo in Q[0] la della coda l accodamento di un nuovo elemento richiede di incrementare il cursore che segna la posizione della fine della coda e di copiare il nuovo elemento nell array, l estrazione di un elemento dalla coda richiede invece n-1 operazioni per riempire lo spazio lasciato libero in. Conservando gli elementi in senso inverso l operazione di estrazione richiede una sola operazione ma l accodamento comporta di spostare gli n elementi presenti. ADT 25 Coda in un array Q Q coda 0 1 2 3 4 5 6 7 8 9 a b g d e f coda 0 1 2 3 4 5 6 7 8 9 b c d e f Testa Q[0] Coda Q[coda] ADT 26 13
Uso di un array circolare Per evitare che accodamento o estrazione richiedano n operazioni basta utilizzare due cursori f e r utilizzando la convenzione che f = r = 0 indichi la coda vuota, in presenza di elementi f indicherà la posizione dell elemento in e r la prima posizione libera in coda. Per evitare che la coda trabocchi in presenza di ripetute operazioni di enqueue e dequeue si deve pensare l array chiuso ad anello utilizzando la posizione Q[0] come se fosse successiva alla posizione Q[n-1] ADT 27 Coda in un array circolare Q 0 1 2 3 4 5 6 7 8 9 g d e f coda Si sono inseriti 6 oggetti e tolti 2 oggetti coda Q 0 k 1 l 2 3 4 5 6 7 8 9 i j Si sono inseriti altri 6 oggetti e tolti 6 oggetti Per distinguere la condizione di coda piena da quella di coda vuota conviene tenere una posizione di guardia e dire: coda vuota quando f = r e coda piena quando (r+1)mod N = f ADT 28 14
Algoritmi per gestire una coda in un array circolare Algoritmo size (): return (N + f + r) mod N Algoritmo isempty (): return (f = r) Algoritmo front (): if isempty() then lancia QueueEmptyException return Q[f] Algoritmo dequeue (): if isempty() then lancia QueueEmptyException tmp Q[f] f (f + 1) mod N return tmp Algoritmo enqueue (o) if (r + 1 ) mod N = f then lancia QueueFullException Q[r] o r (r + 1) mod N ADT 29 Liste concatenate o catene L impiego di una struttura statica come un array per gestire tipi di dati dinamici come stack e code è causa di spreco di risorse o di errori per trabocco. Tali limiti possono essere superati utilizzando come supporto una struttura dinamica composta da celle fra di loro concatenate detta lista concatenata o catena Una catena sarà composta da celle elementari dette nodi ciascuno composto da un oggetto e da un riferimento al prossimo nodo della catena. ADT 30 15
Catene Un nodo della catena elemento riferimento alla prossima cella ADT 31 Node.java nodo per una catena class Node { private Object elemento; private Node prossimo; public Node (Object elemento, Node prossimo) { this.elemento = elemento; this.prossimo = prossimo; void setelement (Object elemento) { this.elemento = elemento; void setnext (Node prossimo) { this.prossimo = prossimo; Object getelement () { return elemento; Node getnext () { return prossimo; ADT 32 16
LinkedStack.java stack in una catena public class LinkedStack implements Stack { private Node top; private int size; public int size () { return size; public boolean isempty () { return (top == ); public void push (Object elemento) { top = new Node(elemento, top); size++; public Object top () throws StackEmptyException { if (isempty()) throw new StackEmptyException("Stack vuoto"); return top.getelement(); public Object pop () throws StackEmptyException { if (isempty()) throw new StackEmptyException("Stack vuoto"); Object tmp = top.getelement(); top = top.getnext(); size--; return tmp; ADT 33 17