22
Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati 2 Tipi di dati astratti Una struttura dati (data structure) è un modo sistematico di organizzare i dati in un contenitore e di controllarne le modalità d’accesso in Java si definisce una struttura dati con una classe Un tipo di dati astratto (ADT, Abstract Data Type) è una rappresentazione astratta di una struttura dati, un modello che specifica: il tipo di dati memorizzati le operazioni che si possono eseguire sui dati il tipo delle informazioni necessarie per eseguire le operazioni 3 Tipi di dati astratti In Java si definisce un tipo di dati astratto con una interfaccia Come sappiamo, un’interfaccia descrive un comportamento che sarà assunto da una classe che realizza l’interfaccia è proprio quello che serve per definire un ADT Un ADT definisce cosa si può fare con una struttura dati che realizza l’interfaccia la classe che rappresenta concretamente la struttura dati definisce invece come vengono eseguite le operazioni 4 Il pacchetto java.util Il pacchetto java.util della libreria standard contiene molte definizioni di ADT come interfacce e loro realizzazioni come classi La nomenclatura e le convenzioni usate in questo pacchetto sono, però, piuttosto diverse da quelle tradizionalmente utilizzate nella teoria dell’informazione (purtroppo e stranamente...) Quindi, proporremo un’esposizione teorica di ADT usando la terminologia tradizionale, senza usare il pacchetto java.util della libreria standard 5 Tipi di dati astratti Un tipo di dati astratto mette in generale a disposizione metodi per svolgere le seguenti azioni inserimento di un elemento rimozione di un elemento ispezione degli elementi contenuti nella struttura ricerca di un elemento all’interno della struttura I diversi ADT che vedremo si differenziano per le modalità di funzionamento di queste tre azioni 6 Pila (stack)

Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

  • Upload
    others

  • View
    1

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 1

1

Strutture dati

2

Tipi di dati astratti✔ Una struttura dati (data structure) è un modo

sistematico di organizzare i dati in un contenitoree di controllarne le modalità d’accesso– in Java si definisce una struttura dati con una classe

✔ Un tipo di dati astratto (ADT, Abstract DataType) è una rappresentazione astratta di unastruttura dati, un modello che specifica:– il tipo di dati memorizzati– le operazioni che si possono eseguire sui dati– il tipo delle informazioni necessarie per eseguire le

operazioni

3

Tipi di dati astratti✔ In Java si definisce un tipo di dati astratto con una

interfaccia✔ Come sappiamo, un’interfaccia descrive un

comportamento che sarà assunto da una classe cherealizza l’interfaccia– è proprio quello che serve per definire un ADT

✔ Un ADT definisce cosa si può fare con unastruttura dati che realizza l’interfaccia– la classe che rappresenta concretamente la struttura dati

definisce invece come vengono eseguite le operazioni

4

Il pacchetto java.util✔ Il pacchetto java.util della libreria standard

contiene molte definizioni di ADT comeinterfacce e loro realizzazioni come classi

✔ La nomenclatura e le convenzioni usate in questopacchetto sono, però, piuttosto diverse da quelletradizionalmente utilizzate nella teoriadell’informazione (purtroppo e stranamente...)

✔ Quindi, proporremo un’esposizione teorica diADT usando la terminologia tradizionale, senzausare il pacchetto java.util della libreria standard

5

Tipi di dati astratti✔ Un tipo di dati astratto mette in generale a

disposizione metodi per svolgere le seguentiazioni– inserimento di un elemento– rimozione di un elemento– ispezione degli elementi contenuti nella struttura

• ricerca di un elemento all’interno della struttura

✔ I diversi ADT che vedremo si differenziano per lemodalità di funzionamento di queste tre azioni

6

Pila (stack)

Page 2: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 2

7

Pila (stack)✔ In una pila (stack) gli oggetti possono essere

inseriti ed estratti secondo un comportamentodefinito LIFO (Last In, First Out)– l’ultimo oggetto inserito è il primo ad essere estratto– il nome è stato scelto in analogia con una pila di piatti

✔ L’unico oggetto che può essere ispezionato èquello che si trova in cima alla pila

✔ Esistono molti possibili utilizzi di una strutturadati con questo comportamento– la JVM usa una pila per memorizzare l’elenco dei

metodi in attesa durante l’esecuzione in un dato istante

8

Pila (stack)✔ I metodi che caratterizzano una pila sono

– push per inserire un oggetto in cima alla pila– pop per eliminare l’oggetto che si trova in cima alla pila– top per ispezionare l’oggetto che si trova in cima alla

pila, senza estrarlo✔ Esiste anche il metodo (non indispensabile)

– topAndPop che combina gli effetti di top e di pop✔ Infine, ogni ADT di tipo “contenitore” ha i metodi

– isEmpty per sapere se il contenitore è vuoto– makeEmpty per vuotare il contenitore

9

Contenitore generico

✔ Anche le interfacce, come le classi, possono essere“estese”

✔ Un’interfaccia eredita tutti i metodi della suasuper-interfaccia

✔ Per realizzare un’interfaccia estesa occorredefinire anche i metodi della sua super-interfaccia

public interface Container{ boolean isEmpty(); void makeEmpty();}

public interface Stack extends Container{ ...}

10

✔ Definiremo tutti gli ADT in modo che possanogenericamente contenere oggetti di tipo Object– in questo modo potremo inserire nel contenitore oggetti

di qualsiasi tipo, perché qualsiasi oggetto può essereassegnato ad un riferimento di tipo Object

Pila (stack)public interface Stack extends Container{ void push(Object obj); void pop(); Object top(); Object topAndPop();}

11

Utilizzo della pila✔ Per evidenziare la potenza della definizione di tipi

di dati astratti come interfacce, supponiamo chequalcun altro abbia progettato la seguente classe

✔ Senza sapere come sia realizzata StackX,possiamo usarne un esemplare mediante il suocomportamento astratto definito in Stack

✔ Allo stesso modo, possiamo usare un esemplare diun’altra classe StackY che realizza Stack

public class StackX implements Stack{ ...}

12

public class ReverseStack // UN ESEMPIO{ public static void main(String[] args) { Stack s = new StackX(); s.push("Pippo"); s.push("Pluto"); s.push("Paperino"); printAndClear(s); System.out.println(); s.push("Pippo"); s.push("Pluto"); s.push("Paperino"); printAndClear(reverseAndClear(s)); } private static Stack reverseAndClear(Stack s) { Stack p = new StackY(); while (!s.isEmpty()) p.push(s.topAndPop()); return p; } private static void printAndClear(Stack s) { while (!s.isEmpty()) System.out.println(s.topAndPop()); }}

PaperinoPlutoPippo

PippoPlutoPaperino

Page 3: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 3

13

Realizzazione della pila✔ Per realizzare una pila è facile ed efficiente usare

una struttura di tipo array “riempito solo in parte”✔ Il solo problema che si pone è cosa fare quando

l’array è pieno e viene invocato il metodo push– la prima soluzione proposta prevede il lancio di

un’eccezione– la seconda soluzione proposta usa il

ridimensionamento dell’array✔ Un altro errore da segnalare con un’eccezione è

l’invocazione di pop o top quando la pila è vuota

14

Eccezioni nella pila✔ Definiamo due eccezioni (che sono classi),

EmptyStackException e FullStackException,come estensione di RuntimeException, in modoche chi usa la pila non sia obbligato a gestirlepublic class EmptyStackException extends RuntimeException{}public class FullStackException extends RuntimeException{}

15

Definizione di un’eccezione

✔ La classe è vuota, a cosa serve?– serve a definire un nuovo tipo di dato (un’eccezione)

che ha le stesse identiche caratteristiche dellasuperclasse da cui viene derivato, ma di cui interessaporre in evidenza il nome, che contiene l’informazionedi identificazione dell’errore

✔ Con le eccezioni si fa spesso così– in realtà la classe non è vuota, perché contiene tutto ciò

che eredita dalla sua superclasse

public class EmptyStackException extends RuntimeException{ }

16

public class FixedArrayStack implements Stack{ // v è un array riempito solo in parte protected Object[] v; // considerare protected protected int vSize; // uguale a private public FixedArrayStack() { v = new Object[100]; // 100 è un numero scelto // a caso, va bene anche 1 // per rendere vuota la struttura, invoco // il metodo makeEmpty, è sempre meglio evitare // di scrivere codice ripetuto makeEmpty(); } // dato che Stack estende Container, // occorre realizzare anche i suoi metodi public void makeEmpty() { vSize = 0; } public boolean isEmpty() { return (vSize == 0); } ...}

17

public class FixedArrayStack implements Stack{ ... public void push(Object obj) { if (vSize == v.length) throw new FullStackException(); v[vSize++] = obj; } public Object top() { if (isEmpty()) throw new EmptyStackException(); return v[vSize - 1]; } public void pop() { if (isEmpty()) throw new EmptyStackException(); vSize--; } public Object topAndPop() { Object obj = top(); pop(); return obj; } // dalla definizione di topAndPop si nota che il // metodo non è necessario (ma è utile)}

18

Intervallo

�����������������������

������

��������������

Page 4: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 4

19

✔ Definiamo una pila che non generi mai l’eccezioneFullStackException

✔ Possiamo evitare di riscrivere tutto il codice diFixedArrayStack in GrowingArrayStack?

public class GrowingArrayStack implements Stack{ public void push(Object obj) { if (vSize == v.length) v = resize(v, 2*vSize); v[vSize++] = obj; } ... // tutto il resto è identico!}

Pila con ridimensionamento

20

✔ Il metodo push sovrascritto deve poter accederealle variabili di esemplare della superclasse

✔ Questo è consentito dalla definizione protected– le variabili protected sono come private, ma vi

si può accedere anche dalle classi derivate (edalle classi che si trovano nello stesso package)

public class GrowingArrayStack extends FixedArrayStack{ public void push(Object obj) { if (vSize == v.length) v = resize(v, 2*vSize); super.push(obj); }}

Pila con ridimensionamento

21

✔ Il progettista della superclasse decide se rendereaccessibile in modo protected lo stato della classe(o una sua parte…)

✔ È una parziale violazione dell’incapsulamento, maavviene in modo consapevole ed esplicito

✔ Anche i metodi possono essere definiti protected– possono essere invocati soltanto all’interno

della classe in cui sono definiti (come i metodiprivate) e all’interno delle classi derivate daessa (nonché all’interno di classi che si trovanonello stesso package)

Accesso protected

22

Prestazioni della pila✔ Il tempo di esecuzione di ogni operazione su una

pila realizzata con array di dimensioni fisse ècostante, cioè non dipende dalla dimensione ndella struttura dati stessa (non ci sono cicli…)– si noti che le prestazioni dipendono dalla

definizione della struttura dati e non dalla suainterfaccia…

– per valutare le prestazioni è necessarioconoscere il codice che realizza le operazioni!

23

Prestazioni della pila✔ Un’operazione eseguita in un tempo costante, cioè

in un tempo che non dipende dalle dimensioni delproblema, ha un andamento asintotico O(1),perché– eventuali costanti moltiplicative vengono

trascurate✔ Ogni operazione eseguita su FixedArrayStack è

quindi O(1)

24

Prestazioni della pila✔ Nella realizzazione con array ridimensionabile,

l’unica cosa che cambia è l’operazione push– “alcune volte” richiede un tempo O(n)

• tale tempo è necessario per copiare tutti gli elementinel nuovo array, all’interno del metodo resize

• il ridimensionamento viene fatto ogni n operazioni

– cerchiamo di valutare il costo medio diciascuna operazione• tale metodo di stima si chiama analisi

ammortizzata delle prestazioni asintotiche

Page 5: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 5

25

Analisi ammortizzata✔ Dobbiamo calcolare il valore medio di n

operazioni, delle quali– n-1 richiedono un tempo O(1)– una richiede un tempo O(n) T(n)= [(n-1)*O(1) + O(n)]/n

= O(n)/n = O(1)

✔ Distribuendo il tempo speso per ilridimensiomento in parti uguali a tutte leoperazioni push, si ottiene quindi ancora O(1)

26

Analisi ammortizzata✔ Le prestazioni di push con ridimensionamento

rimangono O(1) per qualsiasi costantemoltiplicativa usata per calcolare la nuovadimensione, anche diversa da 2

✔ Se, invece, si usa una costante additiva, cioè ladimensione passa da n a n+k, si osserva che su noperazioni di inserimento quelle “lente” sono n/kT(n)= [(n-n/k)*O(1)+(n/k)*O(n)]/n = [O(n) + n*O(n)]/n = O(n)/n + O(n) = O(1) + O(n) = O(n)

27

Pile di oggetti e pile di numeri✔ Abbiamo visto che la pila così definita è in grado

di gestire dati di qualsiasi tipo, cioè riferimenti adoggetti di qualsiasi tipo (stringhe, conti bancari…)

✔ Non è però in grado di gestire dati dei tipifondamentali definiti nel linguaggio Java (int,double, char…)– tali tipi di dati NON sono oggetti e NON possono

essere assegnati a variabili riferimento di tipo Object✔ Come possiamo gestire una pila di numeri?

28

Pile di numeri✔ Possiamo ridefinire tutto

public interface IntStack extends Container{ void push(int obj); void pop(); int top(); int topAndPop();}

public class FixedArrayIntStack implements IntStack{ protected int[] v; protected int vSize; public FixedArrayIntStack() { v = new int[100]; makeEmpty(); } public void makeEmpty() { vSize = 0; } public boolean isEmpty(){ return (vSize == 0); } public void push(int obj) { if(vSize == v.length)throw new FullStackException(); v[vSize++] = obj; } public int top() { if (isEmpty()) throw new EmptyStackException(); return v[vSize - 1]; } public void pop() { if (isEmpty()) throw new EmptyStackException(); vSize--; } public int topAndPop() { int obj = top(); pop(); return obj; }}

29

Pile di numeri✔ La ridefinizione della pila per ogni tipo di dato

fondamentale ha alcuni svantaggi– occorre replicare il codice nove volte (i tipi di dati

fondamentali sono otto), con poche modifiche– non esiste più il tipo di dati astratto Stack, ma esisterà

IntStack, DoubleStack, CharStack, ObjectStack✔ Cerchiamo un’alternativa, ponendoci un problema

– è possibile “trasformare” un numero intero (o un altrotipo di dato fondamentale di Java) in un oggetto?

✔ La risposta è affermativa– si usano le classi involucro (wrapper)

30

Classi involucro✔ Per dati int esiste la classe involucro Integer

– il costruttore richiede un parametro di tipo int e crea unoggetto di tipo Integer che contiene il valore intero,“avvolgendolo” con la struttura di un oggetto

– gli oggetti di tipo Integer sono immutabili– per conoscere il valore memorizzato all’interno di un

oggetto di tipo Integer si usa il metodo non staticointValue, che restituisce un valore di tipo int

Integer intObj = new Integer(2);Object obj = intObj; // lecito

int x = intObj.intValue();// x vale 2

Page 6: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 6

31

Classi involucro✔ Esistono classi involucro per tutti i tipi di dati

fondamentali di Java, con i nomi uguali al nomedel tipo fondamentale ma iniziale maiuscola– eccezioni di nomi: Integer e Character

✔ Ogni classe fornisce un metodo per ottenere ilvalore contenuto al suo interno, con il nomecorrispondente al tipo– es: booleanValue( ), charValue( ), doubleValue( )

✔ Tutte le classi involucro si trovano nel pacchettojava.lang e (tranne Boolean) realizzanoComparable 32

��������������������������

��������������������������������

33

Esercizi

34

✔ Vogliamo risolvere il problema di verificare se inun’espressione algebrica (ricevuta come String) leparentesi tonde, quadre e graffe sono utilizzate inmaniera corretta

✔ In particolare, vogliamo verificare che ad ogniparentesi aperta corrisponda una parentesi chiusadello stesso tipo

✔ Risolviamo prima il problema nel caso semplice incui non siano ammesse parentesi annidate

Esercizio: Controllo parentesi

35

✔ Inizializza la variabile booleana x a false (vale truequando ci si trova all’interno di una coppia di parentesi)

✔ Finché la stringa non è finita– leggi nella stringa il carattere più a sinistra non ancora letto– se è una parentesi

• se è una parentesi aperta– se x è false poni x = true e memorizza il tipo di

parentesi– altrimenti errore (parentesi annidate...)

• altrimenti (è una parentesi chiusa…)– se x è false errore (parentesi chiusa senza

aperta...)– altrimenti se corrisponde a quella memorizzata

poni x = false (la parentesi è stata chiusa…)– altrimenti errore (parentesi non corrispondenti)

✔ Se x è true, errore (parentesi aperta senza chiusa)

Esercizio: Algoritmo

36

public static int checkWithoutNesting(String s){ boolean inBracket = false; char bracket = '0'; // un valore qualsiasi for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (isBracket(c)) if (isOpeningBracket(c)) if (!inBracket) { inBracket = true; bracket = c; } else return 1; else if (!inBracket) return 2; else if(areMatching(bracket, c)) inBracket = false; else return 3; } if (inBracket) return 4; return 0; // OK}

Page 7: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 7

37

private static boolean isOpeningBracket(char c){ return c == '(' || c == '[' || c == '{';}private static boolean isClosingBracket(char c){ return c == ')' || c == ']' || c == '}';}private static boolean isBracket(char c){ return isOpeningBracket(c) || isClosingBracket(c);}private static boolean areMatching(char c1, char c2){ return c1 == '(' && c2 == ')' || c1 == '[' && c2 == ']' || c1 == '{' && c2 == '}';}

38

✔ Cerchiamo di risolvere il caso più generale, in cuile parentesi di vario tipo possono essere annidate

✔ In questo caso non è più sufficiente memorizzareil tipo dell’ultima parentesi che è stata aperta,perché ci possono essere più parentesi aperte chesono in attesa di essere chiuse– quando si chiude una parentesi, bisogna controllare se

corrisponde al tipo della parentesi in attesa che è stataaperta più recentemente

Esercizio: Controllo parentesi

a + [ c + (g + h) + (f + z) ]

39

✔ Possiamo quindi risolvere il problema usando unapila

✔ Effettuando una scansione della stringa da sinistra a destra– inseriamo sulla pila le parentesi aperte– quando troviamo una parentesi chiusa, estraiamo una

parentesi dalla pila (che sarà quindi l’ultima ad esservistata inserita) e controlliamo che i tipi corrispondano,segnalando un errore in caso contrario

• se ci troviamo a dover estrarre da una pila vuota,segnaliamo l’errore (parentesi chiusa senza aperta)

– se al termine della stringa la pila non è vuota,segnaliamo l’errore (parentesi aperta senza chiusa)

Esercizio: Controllo parentesi

40

public static int checkWithNesting(String s){ Stack st = new GrowingArrayStack(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (isBracket(c)) if (isOpeningBracket(c)) st.push(new Character(c)); else try { Object obj = st.topAndPop(); Character ch = (Character)c; char cc = ch.charValue(); if (!areMatching(cc, c)) return 3; } catch (EmptyStackException e) { return 2; } } if (!st.isEmpty()) return 4; return 0;}

41

✔ Abbiamo visto che le strutture dati generiche,definite in termini di Object, sono molto comodeperché possono contenere oggetti di qualsiasi tipo

✔ Sono però un po’ scomode nel momento in cuieffettuiamo l’estrazione (o l’ispezione) di oggettiin esse contenuti– viene sempre restituito un riferimento di tipo Object,

indipendentemente dal tipo di oggetto effettivamenterestituito

– si usa un cast per ottenere un riferimento del tipooriginario Object obj = st.topAndPop();

Character ch = (Character)c;

Estrarre oggetti da una struttura dati

42

✔ Sappiamo che serve il cast perché l’operazione diassegnamento è potenzialmente pericolosa

✔ Il programmatore si assume la responsabilità diinserire nella struttura dati oggetti del tipo corretto

✔ Cosa succede se è stato inserito un oggetto cheNON sia di tipo Character?– viene lanciata l’eccezione ClassCastException

✔ Possiamo scrivere codice che si comporti in modopiù sicuro?

Character ch = (Character)st.topAndPop();

Estrarre oggetti da una struttura dati

Page 8: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 8

43

✔ Ricordiamo che le eccezioni la cui gestione non èobbligatoria, come ClassCastException, possonocomunque essere gestite!

✔ In alternativa si può usare l’operatore instanceof

Estrarre oggetti da una struttura dati

try{ Character ch = (Character)st.topAndPop();} catch (ClassCastException e){ // gestione dell'errore}

Object obj = st.topAndPop();if (obj instanceof Character) Character ch = (Character)obj;else // gestione dell'errore 44

L’operatore instanceof✔ Sintassi:✔ Scopo: è un operatore booleano che restituisce

true se e solo se la variabileOggetto contiene unriferimento ad un oggetto che è un esemplare dellaclasse NomeClasse (o di una sua sottoclasse)– in tal caso l’assegnamento di variabileOggetto

ad una variabile di tipo NomeClasse NONlancia l’eccezione ClassCastException

✔ Nota: il risultato non dipende dal tipo dichiaratoper la variabileOggetto, ma dal tipo dell’oggetto acui la variabile si riferisce effettivamente almomento dell’esecuzione

variabileOggetto instanceof NomeClasse

45

✔ Vogliamo risolvere il problema di calcolare ilrisultato di un’espressione aritmetica (ricevutacome String) contenente somme, sottrazioni,moltiplicazioni e divisioni

✔ Se l’espressione usa la classica notazione (dettainfissa) in cui i due operandi di un’operazione sitrovano ai due lati dell’operatore, l’ordine diesecuzione delle operazioni è determinato dalleregole di precedenza tra gli operatori e daeventuali parentesi

✔ Scrivere un programma per tale compito èpiuttosto complesso, mentre è molto più facilecalcolare espressioni che usano una diversanotazione

Esercizio: Calcolatrice

46

✔ Un’espressione aritmetica può anche usare lanotazione postfissa, detta anche notazione polaccainversa (RPN, Reverse Polish Notation)

✔ In tale notazione non sono ammesse parentesi (nésarebbero necessarie)

✔ I due operandi di ciascun operatore si trovano allasua sinistra

Notazione postfissa

7 1 2 + 4 * 5 6 + - /

7/[(1+2)*4 - (5+6)]

47

✔ Esiste un semplice algoritmo che usa una pila pervalutare un’espressione in notazione postfissa

✔ Finché l’espressione non è terminata– leggi da sinistra il primo simbolo o valore non letto– se è un valore, inseriscilo sulla pila– altrimenti (è un operatore…)

• estrai dalla pila l’operando sinistro• estrai dalla pila l’operando destro• esegui l’operazione• inserisci il risultato sulla pila

✔ Se la pila contiene più di un valore, l’espressionecontiene un errore

✔ L’unico valore presente sulla pila è il risultato

Notazione postfissa

48

public static double evaluateRPN(String s){ Stack st = new GrowingArrayStack(); StringTokenizer tk = new StringTokenizer(s); while (tk.hasMoreTokens()) { String x = tk.nextToken(); try { Double.parseDouble(x); // è un valore numerico st.push(x); } catch (NumberFormatException e) { // è un operatore double r = evalOperator(x, st.topAndPop(), st.topAndPop()); st.push(Double.toString(r)); } } double r = Double.parseDouble(st.topAndPop()); if (!st.isEmpty()) throw new RuntimeException(); return r;}

Page 9: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 9

49

private static double evalOperator(String op, String left, String right){ double opLeft = Double.parseDouble(left); double opRight = Double.parseDouble(right); double result; if (op.equals("+")) result = opLeft + opRight; else if (op.equals("-")) result = opLeft - opRight; else if (op.equals("*")) result = opLeft * opRight; else if (op.equals("/")) result = opLeft / opRight; else throw new RuntimeException(); return result;}

50

Intervallo

�������������������������

��������������

51

Rappresentazione deinumeri

52

Rappresentazione dei numeri✔ Riprendiamo il discorso sulla rappresentazione dei

numeri interi all’interno di un calcolatore✔ Abbiamo visto che per i numeri interi positivi si

usa la rappresentazione binaria posizionale (101100)2 = (44)10

✔ Se si usa una rappresentazione a n bit, si possonorappresentare i 2n numeri interi che sono compresinell’intervallo [0 , 2n - 1] ∩ Ν

53

Numeri interi relativi✔ Come possiamo rappresentare i numeri negativi?

– la rappresentazione più naturale è quella dettarappresentazione con modulo e segno

– si rappresenta il segno positivo o negativo delnumero con il primo bit della sequenza (quellopiù a sinistra), quindi si rappresenta il modulo ovalore assoluto del numero, che ovviamente èun numero non negativo

(101100)2 = (-12)10

(001100)2 = (+12)1054

Numeri interi relativi✔ Se si usa una rappresentazione a n bit, si possono

rappresentare i 2n-1 numeri interi che sonocompresi nell’intervallo [-(2n-1 - 1) , 2n-1 - 1] ∩ Ν

✔ Problema: c’è una doppia rappresentazione per lozero (+0 e -0), per cui si “spreca” unaconfigurazione

✔ Problema: l’algoritmo per l’addizione di numericosì rappresentati è complesso

Page 10: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 10

55

Numeri interi relativi✔ Addizione S = A + B eseguita con numeri

rappresentati in modulo e segno✔ Se segno(A) = segno(B)

segno(S) = segno(A), |S| = ( |A| + |B| ) altrimenti

se |A| ≥ |B| segno(S) = segno(A), |S| =( |A| - |B| )

altrimenti segno(S) = segno(B), |S| =( |B| - |A| )

56

Complemento a due✔ Una rappresentazione più efficiente è quella

denominata complemento a due, così definita– dato un numero intero relativo

a ∈ [-2n-1 , 2n-1 - 1] ∩ Ν la sua rappresentazione in complemento a due è

= C2(a)

rappresentazione binaria a n bit di a se a ≥ 0

rappresentazione binaria a n bit di (a+2n) se a < 0

57

Complemento a due✔ Proprietà (dimostrabili)

– il segno di un numero rappresentato incomplemento a due è ancora il bit più a sinistradella rappresentazione (0 se positivo o nullo, 1se negativo)

– la parte restante della rappresentazione NON èil valore assoluto del numero• lo è soltanto per i numeri positivi

– non ci sono più configurazioni “sprecate”• con n bit si rappresentano 2n numeri diversi

58

Complemento a due✔ Proprietà (dimostrabili)

– per eseguire l’addizione di numeri rappresentatiin complemento a due si esegue semplicementel’addizione binaria delle rappresentazioni,senza pensare al fatto che il bit più a sinistrarappresenta il segno

– al termine dell’addizione, NON bisognaconsiderare un eventuale riporto che si venissea trovare nella posizione n, cioè un’eventuale(n+1)-esima cifra del risultato non ne fa parte

59

Overflow in complemento a due✔ Come per tutte le rappresentazioni numeriche,

anche il complemento a due può dar luogo afenomeni di trabocco (overflow) quando ilrisultato di un’operazione non rientranell’intervallo dei numeri rappresentabili con ilnumero di bit usati dalla rappresentazione

✔ Ad esempio, nell’addizione a+b si ha overflow se

a+b ∉ [-2n-1 , 2n-1 - 1] ∩ Ν✔ Eseguendo l’addizione, come ci si accorge di una

situazione di overflow?60

Overflow in complemento a due✔ Nell’addizione di due numeri in complemento a

due si ha una situazione di overflow se e solo se– si ha un riporto tra la colonna (n-1)-esima e la colonna

n-esima e non si ha un riporto tra la colonna n-esima ela colonna (n+1)-esima

oppure– non si ha un riporto tra la colonna (n-1)-esima e la

colonna n-esima e si ha un riporto tra la colonna n-esima e la colonna (n+1)-esima

✔ Anche questa proprietà è dimostrabile

Page 11: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 11

61

Numeri interi in Java✔ In Java tutti i tipi di dati fondamentali per numeri

interi usano internamente la rappresentazione incomplemento a due– byte, short, int, long

✔ La JVM non segnala le condizioni di overflownelle operazioni aritmetiche– si ottiene semplicemente un risultato errato

✔ L’unica operazione aritmetica tra numeri interi chegenera un’eccezione è la divisione con divisorezero– ArithmeticException

62

Numeri in virgola mobile in Java✔ In Java tutti i tipi di dati fondamentali per numeri

in virgola mobile usano internamente unarappresentazione binaria codificata dallo standardinternazionale IEEE 754– float, double

✔ La divisione con divisore zero non è un errore seeffettuata tra numeri in virgola mobile– se il dividendo è diverso da zero, il risultato è

infinito (con il segno del dividendo)– se anche il dividendo è zero, il risultato non è

un numero e viene usata la codifica specialeNaN (Not a Number)

63

Numeri in virgola mobile in Java✔ Lo standard IEEE 754 prevede quindi anche la

rappresentazione di NaN, di +∞ e di -∞✔ Sono definite le seguenti costanti

– Double.NaN– Double.NEGATIVE_INFINITY– Double.POSITIVE_INFINITY

✔ e le corrispondenti costanti Float– Float.NaN– Float.NEGATIVE_INFINITY– Float.POSITIVE_INFINITY

64

��������������������������

�������������������������������

65

Coda (queue)

66

Coda (queue)✔ In una coda (queue) gli oggetti possono essere

inseriti ed estratti secondo un comportamentodefinito FIFO (First In, First Out)– il primo oggetto inserito è il primo ad essere estratto– il nome è stato scelto in analogia con persone in coda

✔ L’unico oggetto che può essere ispezionato èquello che verrebbe estratto

✔ Esistono molti possibili utilizzi di una strutturadati con questo comportamento– la simulazione del funzionamento di uno sportello

bancario con più clienti che arrivano in momenti diversiuserà una coda per rispettare la priorità di servizio

Page 12: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 12

67

Coda (queue)✔ I metodi che caratterizzano una coda sono

– enqueue per inserire un oggetto nella coda– dequeue per esaminare ed eliminare dalla coda

l’oggetto che vi si trova da più tempo– getFront per esaminare l’oggetto che verrebbe

eliminato da dequeue, senza estrarlo

✔ Infine, ogni ADT di tipo “contenitore” ha i metodi– isEmpty per sapere se il contenitore è vuoto– makeEmpty per vuotare il contenitore

68

✔Si notino le similitudini con la pila– enqueue corrisponde a push– dequeue corrisponde a topAndPop– getFront corrisponde a top– pop non ha un metodo analogo nella coda

Coda (queue)public interface Queue extends Container{ void enqueue(Object obj); Object dequeue(); Object getFront();}

69

Coda (queue)✔ Per realizzare una coda si può usare una struttura

di tipo array “riempito solo in parte”, in modosimile a quanto fatto per realizzare una pila

✔ Mentre nella pila si inseriva e si estraeva allostesso estremo dell’array (l’estremo “destro”), quidobbiamo inserire ed estrarre ai due diversiestremi– decidiamo di inserire a destra ed estrarre a sinistra

70

Coda (queue)✔ Come per la pila, anche per la coda bisognerà

segnalare l’errore di accesso ad una coda vuota egestire la situazione di coda piena (segnalando unerrore o ridimensionando l’array)

✔ Definiamo– EmptyQueueException e FullQueueException

public class EmptyQueueException extends RuntimeException{ }

public class FullQueueException extends RuntimeException{ }

71

public class SlowFixedArrayQueue implements Queue{ private Object[] v; private int vSize; public SlowFixedArrayQueue() { v = new Object[100]; makeEmpty(); } public void makeEmpty() { vSize = 0; } public boolean isEmpty() { return (vSize == 0); } public void enqueue(Object obj) { if (vSize == v.length) throw new FullQueueException(); v[vSize++] = obj; } public Object getFront() { if (isEmpty()) throw new EmptyQueueException(); return v[0]; } public Object dequeue() { Object obj = getFront(); vSize--; for (int i = 0; i < vSize; i++) v[i] = v[i+1]; return obj; }}

72

Coda (queue)✔ Questa semplice realizzazione con array, che

abbiamo visto essere molto efficiente per la pila, èal contrario assai inefficiente per la coda– il metodo dequeue è O(n), perché bisogna

spostare tutti gli oggetti della coda per fare inmodo che l’array rimanga “compatto”

– la differenza rispetto alla pila è dovuta al fattoche nella coda gli inserimenti e le rimozioniavvengono alle due estremità diverse dell’array,mentre nella pila avvengono alla stessaestremità

Page 13: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 13

73

Coda (queue)✔ Per realizzare una coda più efficiente servono due

indici anziché uno soltanto– un indice punta al primo oggetto della coda e

l’altro indice punta all’ultimo oggetto dellacoda

✔ In questo modo, aggiornando opportunamente gliindici, si ottiene la realizzazione di una coda conun “array riempito solo nella parte centrale” incui tutte le operazioni sono O(1)– la gestione dell’array pieno ha le due solite

soluzioni, ridimensionamento o eccezione

74

public class FixedArrayQueue implements Queue{ protected Object[] v; protected int front, back; public FixedArrayQueue() { v = new Object[100]; makeEmpty(); } public void makeEmpty() { front = back = 0; } public boolean isEmpty() { return (back == front); } public void enqueue(Object obj) { if (back == v.length) throw new FullQueueException(); v[back++] = obj; } public Object getFront() { if (isEmpty()) throw new EmptyQueueException(); return v[front]; } public Object dequeue() { Object obj = getFront(); front++; return obj; }}

75

public class GrowingArrayQueue extends FixedArrayQueue{ public void enqueue(Object obj) { if (back == v.length) v = resize(v, 2*v.length); super.enqueue(obj); }}

Coda (queue)✔ Per rendere la coda ridimensionabile, usiamo la

stessa strategia vista per la pila, estendendo laclasse FixedArrayQueue e sovrascrivendo il solometodo enqueue

76

Prestazioni della coda✔ La realizzazione di una coda con un array e due

indici ha la massima efficienza in termini diprestazioni temporali, tutte le operazioni sonoO(1), ma ha ancora un punto debole

✔ Se l’array ha N elementi, proviamo a– effettuare N operazioni enqueue

e poi– effettuare N operazioni dequeue

✔ Ora la coda è vuota, ma alla successivaoperazione enqueue l’array sarà pieno– lo spazio di memoria non viene riutilizzato

77

Coda con array circolare✔ Per risolvere quest’ultimo problema si usa una

tecnica detta “array circolare”– i due indici, dopo essere giunti alla fine

dell’array, possono ritornare all’inizio se sisono liberate delle posizioni

– in questo modo l’array risulta pieno solo se lacoda ha effettivamente un numero di oggettiuguale alla dimensione dell’array

– le prestazioni temporali rimangono identiche

78

public class FixedCircularArrayQueue extends FixedArrayQueue{ // il metodo increment fa avanzare un indice di una // posizione, tornando all’inizio dell’array se si supera // la fine protected int increment(int index) { return (index + 1) % v.length; } public void enqueue(Object obj) { if (increment(back) == front) throw new FullQueueException(); v[back] = obj; back = increment(back); } public Object dequeue() { Object obj = getFront(); front = increment(front); return obj; }}

Coda con array circolare

Page 14: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 14

79

public class GrowingCircularArrayQueue extends FixedCircularArrayQueue{ public void enqueue(Object obj) { if (increment(back) == front) { v = resize(v, 2*v.length); // se si ridimensiona l’array e la zona utile // della coda si trova attorno alla sua fine, // la seconda metà del nuovo array rimane vuota // e provoca un malfunzionamento della coda, // che si risolve spostandovi la parte della // coda che si trova all’inizio dell’array if (back < front) { System.arraycopy(v, 0, v, v.length/2, back); back += v.length/2; } } super.enqueue(obj); }}

Coda con array circolare

80

Intervallo

�������������������������

��������������

81

Passaggio di parametri pervalore e per riferimento

82

✔ Abbiamo visto il metodo increment che ha ilcompito di fornire un nuovo valore per unavariabile di tipo numerico

✔ Perché non abbiamo scritto più semplicemente

// si usa con x = increment(x)protected int increment(int index){ return (index + 1) % v.length;}

Modificare parametri numerici

// si usa con increment(x)protected void increment(int index){ index = (index + 1) % v.length;}

83

Modificare parametri numerici// si usa con increment(x)protected void increment(int index){ index = (index + 1) % v.length;} // NON FUNZIONA

x

index

invocando il metodo il valore viene copiato

in increment si modificaquesto valorequando si torna nel metodo invocante

il valore di x non è stato modificato

84

// NON FUNZIONApublic static void swapAccounts(BankAccount x, BankAccount y){ BankAccount temp = x; x = y; y = temp;}

Modificare parametri numerici✔ Abbiamo visto che un metodo può invece

modificare lo stato di un oggetto passato comeparametro (implicito o esplicito)– ma non può modificare il riferimento contenuto nella

variabile oggetto che ne costituisce il parametro effettivo

swapAccounts(a, b);// nulla è successo alle// variabili a e b

Page 15: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 15

85

Chiamate per valore e per riferimento

✔ In Java, il passaggio dei parametri è effettuato “pervalore”, cioè il valore del parametro effettivo vieneassegnato al parametro formale– questo impedisce che il valore contenuto nella variabile

che costituisce il parametro effettivo possa esseremodificato

✔ Altri linguaggi di programmazione (come C++)consentono di effettuare il passaggio dei parametri“per riferimento”, rendendo possibile la modificadei parametri effettivi

86

Chiamate per valore e per riferimento

✔ A volte si dice, impropriamente, che in Java inumeri sono passati per valore e che gli oggettisono passati per riferimento

✔ In realtà, tutte le variabili sono passate per valore,ma– passando per valore una variabile oggetto, si passa una

copia del riferimento in essa contenuto– l’effetto “pratico” del passaggio per valore di un

riferimento ad un oggetto è la possibilità di modificarelo stato dell’oggetto stesso, come avviene con ilpassaggio “per riferimento”

87

Ordinare pile e code

88

✔ Proviamo a realizzare l’ordinamento per fusione(Mergesort) applicato ad una pila anziché ad unarray– ovviamente la pila deve contenere oggetti Comparable

✔ Il primo problema consiste nella suddivisionedella pila in due pile di dimensioni circa uguali– non conosciamo la dimensione della pila– una soluzione semplice consiste nell’estrarre gli

elementi dalla pila ed inserirli alternativamentenelle due semi-pile

Esercizio: Mergesort per pila

89

public static void mergeSort(Stack s){ if (s == null || s.isEmpty()) return; // caso base Object temp = s.topAndPop(); if (s.isEmpty()) { s.push(temp); // altro caso base: dimensione 1 return; } // dividiamo (circa) a meta’ Stack left = new GrowingArrayStack(); Stack right = new GrowingArrayStack(); left.push(temp); boolean flag = true; while (!s.isEmpty()) if (flag = !flag) // attenzione al trucco... left.push(s.topAndPop()); else right.push(s.topAndPop()); // passi ricorsivi per problemi piu’ semplici mergeSort(left); mergeSort(right); // fusione: si osservi che ora s è vuota merge(s, left, right);}

90

private static void merge(Stack s, // NON FUNZIONA Stack left, Stack right){ while (!left.isEmpty() && !right.isEmpty()) { Comparable x = (Comparable) left.top(); Comparable y = (Comparable) right.top(); if (x.compareTo(y) < 0) s.push(left.topAndPop()); else s.push(right.topAndPop()); } while (!left.isEmpty()) s.push(left.topAndPop()); while (!right.isEmpty()) s.push(right.topAndPop()); // a questo punto s contiene i dati ordinati, // ma in cima alla pila c’è l’elemento maggiore!}

Fusione di due pile ordinate

Page 16: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 16

91

private static void merge(Stack s, Stack left, Stack right){ Stack temp = new GrowingArrayStack(); while (!left.isEmpty() && !right.isEmpty()) { Comparable x = (Comparable) left.top(); Comparable y = (Comparable) right.top(); if (x.compareTo(y) < 0) temp.push(left.topAndPop()); else temp.push(right.topAndPop()); } while (!left.isEmpty()) temp.push(left.topAndPop()); while (!right.isEmpty()) temp.push(right.topAndPop()); while (!temp.isEmpty()) s.push(temp.topAndPop());}

Fusione di due pile ordinate

92

✔ Notiamo che, nonostante l’apparente maggiorecomplessità, le prestazioni asintotiche rimangonoO(n lg n)

✔ L’analisi dell’algoritmo (ricordando che tutte leoperazioni sulle pile sono O(n) ) fornisce ancora

T(n) = 2T(n/2) + O(n) da cui si ottiene di nuovo

O(n lg n)

Esercizio: Mergesort per pila

93

✔ Proviamo a realizzare l’ordinamento per fusione(Mergesort) applicato ad una coda anziché ad unarray– ovviamente la coda deve contenere oggetti

Comparable✔ La soluzione del problema è molto simile alla

soluzione vista per la pila

Esercizio: Mergesort per coda

94

public static void mergeSort(Queue q){ if (q == null || q.isEmpty()) return; // caso base Object temp = q.dequeue(); if (q.isEmpty()) { q.enqueue(temp); // altro caso base: dimensione 1 return; } // dividiamo (circa) a meta’ Queue left = new GrowingCircularArrayQueue(); Queue right = new GrowingCircularArrayQueue(); left.enqueue(temp); boolean flag = true; while (!q.isEmpty()) if (flag = !flag) left.enqueue(q.dequeue()); else right.enqueue(q.dequeue()); // passi ricorsivi per problemi piu’ semplici mergeSort(left); mergeSort(right); // fusione: si osservi che ora q è vuota merge(q, left, right);}

95

private static void merge(Queue q, Queue left, Queue right){ while (!left.isEmpty() && !right.isEmpty()) { Comparable x = (Comparable) left.getFront(); Comparable y = (Comparable) right.getFront(); if (x.compareTo(y) < 0) q.enqueue(left.dequeue()); else q.enqueue(right.dequeue()); } while (!left.isEmpty()) q.enqueue(left.dequeue()); while (!right.isEmpty()) q.enqueue(right.dequeue());}

Fusione di due code ordinate

96

✔ Di nuovo, le prestazioni asintotiche sono O(n lg n)

✔ L’analisi dell’algoritmo (ricordando che tutte leoperazioni sulle code sono O(n) ) fornisce ancora

T(n) = 2T(n/2) + O(n) da cui si ottiene di nuovo

O(n lg n)

Esercizio: Mergesort per coda

Page 17: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 17

97

��������������������������������

������������������������������������

98

Esercizio

99

✔ Vogliamo risolvere il problema di determinaretutte le possibili permutazioni dei caratteri presentiin una stringa– facciamo l’ipotesi che non ci siano caratteri ripetuti

✔ Ricordiamo dalla matematica combinatoria che ilnumero di permutazioni di N simboli è N!

✔ Esempio: ABC– ABC ACB BAC BCA CAB CBA

Esercizio: Permutazioni

100

✔ Esiste un semplice algoritmo ricorsivo per trovarele permutazioni di una stringa di lunghezza N

✔ Se N vale 1, l’unica permutazione è la stringastessa

✔ Altrimenti– estrai il primo carattere dalla stringa– calcola le (N-1)! permutazioni della stringa rimanente– per ognuna delle permutazioni (incomplete) ottenute

• genera N permutazioni (complete) inserendo il carattereprecedentemente estratto in ognuna delle posizioni possibilinella permutazione incompleta

Esercizio: Permutazioni

101

✔ Analizziamo meglio questo punto– calcola le (N-1)! permutazioni della stringa rimanente– per ognuna delle permutazioni (incomplete) ottenute

• genera N permutazioni (complete) inserendo il carattereprecedentemente estratto in ognuna delle posizioni possibilinella permutazione incompleta

✔ Le posizioni in cui si può inserire un carattere inuna delle permutazioni incomplete, che hadimensione N-1, sono le N-2 posizioni che sitrovano tra due caratteri, più la posizione iniziale ela posizione finale– sono quindi N posizioni diverse

Esercizio: Permutazioni

102

public static String[] permutations(String p){ // gestiamo i casi base if (p == null || p.length() == 0) return new String[0]; // oppure return null if (p.length() == 1) return new String[] {p}; // isoliamo il primo carattere String c = p.substring(0,1); // passo ricorsivo String[] cc = permutations(p.substring(1)); // numero di permutazioni da generare String[] r = new String[p.length() * cc.length]; for (int i = 0; i < p.length(); i++) for (int j = 0; j < cc.length; j++) { String s = cc[j]; String sLeft = s.substring(0,i); String sRight = s.substring(i); r[i*cc.length+j] = sLeft + c + sRight; } return r;}

Page 18: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 18

103

✔ Alcuni commenti generali sulla gestione dellecondizioni “eccezionali”– il metodo riceve un riferimento ad una stringa: tale

riferimento può essere null oppure la stringa può esserevuota (cioè avere lunghezza zero)

• in entrambi i casi il metodo ricorsivo nonfunzionerebbe correttamente, quindi li inseriamocome casi base della ricorsione

– cosa restituiamo?• quando un metodo deve restituire un oggetto, nei

casi in cui riceve un parametro non corretto di solitorestituisce null

Esercizio: Permutazioni

if (p == null || p.length() == 0) return null; 104

✔ In questo caso il metodo deve restituire un oggettodi tipo un po’ particolare, in quanto è un array

✔ Sarebbe comunque corretto restituire null, ma disolito si preferisce restituire un array di lunghezzazero (perfettamente lecito), in modo che il metodoinvocante riceva un array valido, seppure vuoto

✔ In questo modo, il codice seguente funziona…

mentre non funzionerebbe se restituissimo null

Esercizio: Permutazioni

String[] x = permutations("");for (int i = 0; i < x.length; i++) System.out.println(x[i]);

if (p == null || p.length() == 0) return new String[0];

105

✔ Notiamo che anche l’ordine in cui vengonovalutate le due condizioni è molto importante

✔ Se p è null, la prima condizione è vera e per lastrategia di cortocircuito nella valutazionedell’operatore || la seconda condizione nonviene valutata– se venisse valutata, verrebbe lanciata

l’eccezione NullPointerException !!

Esercizio: Permutazioniif (p == null || p.length() == 0) return new String[0];

106

✔ Per calcolare la dimensione del vettore checonterrà le permutazioni, sfruttiamo leinformazioni ottenute dall’invocazione ricorsiva– il numero di permutazioni è uguale alla dimensione

della stringa moltiplicata per il numero di permutazioni(incomplete) già generate

Esercizio: Permutazioni// isoliamo il primo carattereString c = p.substring(0,1);// passo ricorsivoString[] cc = permutations(p.substring(1));// numero di permutazioni da generareString[] r = new String[p.length() * cc.length];

107

✔ Per completare le permutazioni inseriamo ilcarattere c in tutte le posizioni possibili inciascuna permutazione incompleta s

✔ Per ogni s, calcoliamo le sottostringhe cheverranno concatenate a sinistra e a destra di c

✔ Sfruttiamo il fatto che il metodo substringgestisce in modo congruente le situazioni“anomale”

Esercizio: Permutazionifor (int i = 0; i < p.length(); i++) for (int j = 0; j < cc.length; j++) { String s = cc[j]; String sLeft = s.substring(0,i); String sRight = s.substring(i); r[i*cc.length+j] = sLeft + c + sRight; }

108

✔ Quando i vale 0, substring restituisce una stringavuota, che è proprio ciò che vogliamo

✔ Quando i vale p.length( )-1 (suo valore massimo),vale anche s.length( )– in questo caso particolare, substring non lancia

eccezione, ma restituisce una stringa vuota, che èproprio ciò che vogliamo

Esercizio: PermutazioniString sLeft = s.substring(0,i);

String sRight = s.substring(i);

Page 19: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 19

109

✔ Analizziamo meglio questa espressione dell’indice✔ Globalmente, tale indice deve andare da 0 ap.length() * cc.length (escluso)

✔ Verifichiamo innanzitutto i limiti– per i = 0 e j = 0, l’indice vale 0– per i = p.length()-1 e j = cc.length-1,

l’indice vale (p.length()-1)*cc.length + cc.length - 1 =

p.length()*cc.length - 1 (come volevamo)

Esercizio: Permutazionir[i*cc.length+j] = sLeft + c + sRight;

110

✔ Alla prima iterazione di i, l’indice varia tra 0 ecc.length-1 (perché i vale 0)

✔ Alla seconda iterazione di i, l’indice varia tra1*cc.length+0 = cc.length e1*cc.length+cc.length-1 =2*cc.length-1

✔ Si osserva quindi che gli indici vengono generaticonsecutivamente, senza nessun valore mancante esenza nessun valore ripetuto

Esercizio: Permutazionir[i*cc.length+j] = sLeft + c + sRight;

111

✔ Proviamo ora ad estendere il problema, percalcolare le permutazioni di oggetti genericianziché di caratteri in una stringa

✔ L’algoritmo sarà lo stesso, ma il metodo– riceverà un array di N oggetti anziché una stringa– dovrà restituire un array bidimensionale di oggetti, con

N! righe e N colonne• ciascuna riga conterrà una permutazione

Esercizio: Permutazioni

112

public static Object[][] permutations(Object[] p){ if (p == null || p.length == 0) return new Object[0][0]; if (p.length == 1) { Object[][] r = new Object[1][1]; r[0][0] = p[0]; return r; } Object c = p[0]; Object[] p2 = new Object[p.length-1]; System.arraycopy(p, 1, p2, 0, p2.length); Object[][] cc = permutations(p2); Object[][] r = new Object[p.length*cc.length][p.length]; for (int i = 0; i < p.length; i++) for (int j = 0; j < cc.length; j++) { int index = i * cc.length + j; for (int k = 0; k < i; k++) r[index][k] = cc[j][k]; r[index][i] = c; for (int k = i; k < p.length - 1; k++) r[index][k+1] = cc[j][k]; } return r;}

113

Intervallo

�������������������

���

������������

114

Il metodo equals

Page 20: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 20

115

✔ Tutto quanto visto sulle permutazioni funzionacorrettamente se è valida l’ipotesi iniziale– gli oggetti da permutare sono tutti distinti

✔ In caso contrario, le permutazioni generate nonsono tutte distinte– ci poniamo il problema di verificare, all’inizio del

programma, che tutti gli oggetti dell’insieme sianodistinti

✔ Più in generale, risolviamo il problema dideterminare se in un array di oggetti esistonooggetti ripetuti, cioè (per essere più precisi)oggetti con identiche proprietà di stato

Esercizio: Oggetti distinti

116

✔ Questo metodo però non funziona correttamente– verifica se nell’array esistono riferimenti che

puntano allo stesso oggetto

public static boolean areUnique(Object[] p){ if (p == null) return true; for (int i = 0; i < p.length; i++) // ogni oggetto viene confrontato // con tutti i successivi for (int j = i+1; j < p.length; j++) if (p[i] == p[j]) return false; return true;}

Esercizio: Oggetti distinti

117

Esercizio: Oggetti distinti✔ Per verificare, invece, che non esistano riferimenti

che puntano ad oggetti (eventualmente diversi)con le medesime proprietà di stato, occorreconfrontare il contenuto (lo stato) degli oggettistessi, e non solo i loro indirizzi in memoria

✔ Lo stato degli oggetti non è generalmenteaccessibile dall’esterno dell’oggetto stesso

✔ Come risolvere il problema?– sappiamo che per le classi della libreria

standard possiamo invocare il metodo equals118

Il metodo equals✔ Come è possibile che il metodo equals venga invocato per

qualsiasi tipo di oggetto?– il metodo equals è definito nella classe Object

✔ Essendo definito in Object, equals viene ereditato da tuttele classi Java, quindi può essere invocato con qualsiasioggetto, come toString– il comportamento ereditato, se non sovrascritto, svolge

la stessa funzione del confronto tra i riferimenti cheabbiamo visto prima

public boolean equals(Object obj){ return (this == obj);}

119

Sovrascrivere il metodo equals✔ Per consentire il confronto per uguaglianza tra due

oggetti di una classe in modo che venga esaminatolo stato degli oggetti stessi, occorre sovrascrivereil metodo equals

public class BankAccount{ public boolean equals(Object obj) { BankAccount other = (BankAccount) obj; return (balance == other.balance); } ...}

120

✔ Questo metodo funziona correttamente se glioggetti appartengono a classi che hannosovrascritto il metodo equals– non ci sono alternative

public static boolean areUnique(Object[] p){ if (p == null) return true; for (int i = 0; i < p.length; i++) // ogni oggetto viene confrontato // con tutti i successivi for (int j = i+1; j < p.length; j++) if (p[i].equals(p[j])) return false; return true;}

Esercizio: Oggetti distinti

Page 21: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 21

121

Gestione di file in Java

122

Gestione di file in Java✔Finora abbiamo visto programmi Java che

interagiscono con l’utente soltanto tramite iflussi di ingresso e di uscita standard– ciascuno di tali flussi può essere collegato ad un

file con un comando di sistema operativo✔Ci chiediamo: è possibile leggere e scrivere

file in un programma Java?– con la redirezione di input/output, ad esempio,

non possiamo leggere da due file o scrivere sudue file...

123

Gestione di file in Java✔Limitiamoci ad affrontare il problema della

gestione di file di testo (file contenenticaratteri)– esistono anche i file binari, che contengono

semplicemente configurazioni di bit cherappresentano qualsiasi tipo di dati

✔La gestione dei file avviene interagendo conil sistema operativo mediante classi delpacchetto java.io della libreria standard

124

✔Prima di leggere caratteri da un file(esistente) occorre aprire il file in lettura– questa operazione si traduce in Java nella

creazione di un oggetto di tipo FileReader

– il costruttore necessita del nome del file sottoforma di stringa

– se il file non esiste, viene lanciata l’eccezioneFileNotFoundException, che deve esseregestita

Lettura di file di testo

FileReader reader = new FileReader("file.txt");

125

✔ Con l’oggetto di tipo FileReader si può invocareil metodo read che restituisce un carattere ad ogniinvocazione, iniziando dal primo carattere del filee procedendo fino alla fine del file stesso

✔ Non è possibile tornare indietro e rileggerecaratteri già letti– bisogna creare un nuovo oggetto di tipo FileReader

Lettura di file di testo

FileReader reader = new FileReader("file.txt");while(true){ int x = reader.read(); // read restituisce un if (x == -1) break; // intero che vale -1 char c = (char) x; // se il file è finito // elabora c} // il metodo lancia IOException, da gestire

126

✔ Al termine della lettura del file (che nonnecessariamente procede fino alla fine…) occorrechiudere il file

✔ Anche questo metodo lancia IOException, dagestire obbligatoriamente

✔ Se non viene invocato non si ha un errore, ma unapotenziale situazione di instabilità per il sistemaoperativo

Lettura di file di testo

FileReader reader = new FileReader("file.txt");...reader.close();

Page 22: Il pacchetto java - Libero.itspazioinwind.libero.it/inginfotv/appunti/fondinfo1/pdf/... · 2002-01-16 · Fondamenti di Informatica 1 Settimana 6 Marcello Dalpasso 1 1 Strutture dati

Fondamenti di Informatica 1 Settimana 6

Marcello Dalpasso 22

127

✔Prima di scrivere caratteri in un file occorreaprire il file in scrittura– questa operazione si traduce in Java nella

creazione di un oggetto di tipo FileWriter

– il costruttore necessita del nome del file sottoforma di stringa e può lanciare l’eccezioneIOException, che deve essere gestita• se il file non esiste, viene creato• se il file esiste, il suo contenuto viene

sovrascritto con i nuovi contenuti

Scrittura di file di testo

FileWriter writer = new FileWriter("file.txt");

128

✔ Con l’oggetto di tipo FileWriter si può invocare ilmetodo write, un metodo sovraccarico checonsente di scrivere– singoli caratteri– array di caratteri– stringhe

✔ Anche questo metodo lancia IOException, dagestire obbligatoriamente

✔ Diversamente da print/println, però, write nonaccetta generici oggetti come parametri, per cuioccorre invocare esplicitamente toString

Scrittura di file di testo

129

✔ Al termine della scrittura del file occorre chiudereil file

✔ Anche questo metodo lancia IOException, dagestire obbligatoriamente

✔ Se non viene invocato non si ha un errore, ma èpossibile che la scrittura del file non vengaultimata prima della terminazione delprogramma, lasciando il file incompleto

Scrittura di file di testo

FileWriter writer = new FileWriter("file.txt");...writer.close();

130

Gestione di file in Java✔Usando le classi FileReader e FileWriter

del pacchetto java.io è quindi possibilemanipolare, all’interno di un programmaJava, più file in lettura e/o più file inscrittura

✔Rimane invariata la possibilità di utilizzare iflussi di ingresso e di uscita standard, chepossono in realtà essere collegati a filesenza che il programma Java ne siaconsapevole

131

������������������������

����������

������������������������������������