Laboratorio VIII: 26-Nov-2007                                         versione del 25-Nov-2007
                                                                                                                                                                                                                                         

Parte I
HOW TO

Comprendere gli Avvisi del compilatore
Il compilatore talvolta produce dei messaggi detti warnings (avvisi).
Si scarichino, ad esempio,  la classe RawComparableContainer.java  e l'interfaccia Container.java.
Si compili la classe:

$javac RawComparableContainer.java
Note: RawComparableContainer.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.


Non sono errori di compilazione! Sono solo segnalazioni (avvisi).  Infatti il compilatore ha prodotto il bytecode RawComparableContainer.class.

Approfondiamo le cause di questo comportamento. Il compilatore segnala di ricompilare con l'opzione -Xlint, facciamolo:

$javac RawComparableContainer.java -Xlint

RawComparableContainer.java:132: warning: [unchecked] unchecked call to compareTo(T) as a member of the raw type java.lang.Comparable
         if (b[ib].compareTo(c[ic]) < 0)
                            ^

1 warning

Il compilatore ci avvisa che le variabili riferimento b[ib] e c[ic] potrebbero non appartenere alla stessa classe. Il compilatore sa, infatti, che entrambi sono di tipo Comparable, ma non sa se appartengono alla medesima classe. Ad esempio, entrambe le classi java.lang.String e java.lang.Integer realizzano l'interfaccia java.lang.Comparable. Se, per errore del programmatore, il metodo compareTo() viene invocato con due parametri (implicito e esplicito) di classi diverse, generera' l'eccezione java.lang.ClassCastException.




Correggere gli errori logici
Il compilatore segnala gli errori sintattici e un piccolo insieme di errori semantici (ovvero logici).

Per trovare gli errori semantici non segnalati dal compilatore i programmatori si avvalgono di  programmi appositi, detti "debugger", che permettono di:
- ispezionare il valore delle variabili;
- eseguire il codice passo dopo passo, ovvero eseguire un enunciato e poi interrompere l'esecuzione;
- marcare uno o piu' enunciati con punti di arresto (breakpoints); il programma si arresta prima dell'esecuzione dell'enunciato marcato con punto di arresto in modo che il programmatore possa verificare il valore delle variabili.
Il comando jdb avvia il debugger del Sun jdk.
In Bluj, nel menu' tools il comando Set/Clear Breakpoint serve per introdurre/cancellare un punto di arresto. Se siete curiosi provate.

In questo corso non approfondiremo l'uso di debugger.

Per correggere gli errori logici abbiamo, allora, a disposizione un solo modo: far stampare al codice dei messaggi a standard output in sezioni critiche del codice, per verificare il valore delle variabili.
Impariamo a inserire nei metodi critici delle stampe per documentare l'evoluzione del programma.
Ad esempio nel metodo add() di un contenitore, dopo aver aggiunto un oggetto obj inseriamo gli enunciati:
// Stampe di debugging
System.out.println("add(): inserito l'oggetto " + obj);
System.out.println("Oggetti contenuti: ")
for (int i = 0; i < size; i++)
   System.out.println(i + " " + a[i]);  // dove a e' l'array contenente gli oggetti

Il primo enunciato documenta l'inserimento appena effettuato, il secondo documenta lo stato dell'array a dopo l'ultimo inserimento. Le stampe vanno commentate nella versione finale del software:
/*
// Stampe di debugging
System.out.println("add(): inserito l'oggetto" + obj);
System.out.println("Oggetti contenuti: ")
for (int i = 0; i < size; i++)
   System.out.println(i + " " + a[i]);  //
dove a e' l'array contenente gli oggetti
*/
Questo ci aiuta a identificare il comportamento dei metodi.


Parte II
Linguaggio Java: classi, ordinamento, ricerca di oggetti specifici
La mia soluzione
Ordinare Stringhe
L'interfaccia Container.java definisce in astratto un contenitore.
Codificare la classe TextContainer, che realizza l'interfaccia Container, la cui interfaccia pubblica è documentata in TextContainer.html. La classe, che e' in grado di memorizzare le parole di un testo (contenitore di testo), realizzi il metodo void sort() per ordinare le parole in senso lessicografico crescente. Il metodo sia ottimizzato per l'ordinamento di un numero elevato di parole.

Si scriva poi la classe TextContainerTester che:

- definisce un esemplare della classe TextContainer e lo inizializzi con parole acquisite da standard input.  Una parola sia una stringa delimitata dai caratteri delimitatori  whitespaces e/o dai seguenti caratteri d'interpunzione: .,;:-!?();

- ordina gli elementi del contenitore in senso lessicografico;

- elimina le parole eventualmente ripetute. Si noti che nel contenitore ordinato le parole ripetute sono vicine. Per eliminare le ripetizioni, si crei un nuovo contenitore dove inserire solo le parole senza ripetizione (prima di inserire verificare che la parola non sia uguale all'ultima inserita).

- stampa a standard output le parole del testo nell'ordine in cui compaiono nel testo originale, precedute da una stampa del numero di parole. Esempio:
*** STAMPA DELLE PAROLE IN ORDINE DI ACQUISIZIONE ***
Numero delle parole nel testo originale: 3
>>> rosso
>>> verde
>>> rosso

- stampa a standard output le parole del testo ordinate in senso lessicografico crescente, precedute da una stampa del numero di parole. Esempio:
*** STAMPA DELLE PAROLE IN ORDINE LESSICOGRAFICO ***
Numero delle parole nel testo ordinato: 3
>>> rosso
>>> rosso
>>> verde

- stampa a standard output le parole del testo ordinate in senso lessicografico crescente, senza ripetizioni, precedute da una stampa del numero di parole. Esempio:
*** STAMPA DELLE SINGOLE OCCORRENZE IN ORDINE LESSICOGRAFICO ***
Numero delle parole non ripetute: 2
>>> rosso
>>> verde

Provare la classe acquisendo i dati dal file alberi.txt, mediante redirezione dello standard input.
Esempio di uso: $java TextTester < nomeInputFile

NOTA: per estrarre elementi dal contenitore e' disponibile solo il metodo removeLast() che ha due particolarita':
- rimuove l'ultimo elemento del contenitore
- estrae l'elemento dal contenitore
Per estrarre gli elementi da un contenitore text senza perdere l'informazione contenuta bisogna programmare come segue:
...
TextContainer inverseText = new TextContainer();
while (!text.isEmpty())
{
   String word = text.removeLast();
   inverseText.add(word);
   // ... elaborazione di word
}
...

Il contenitore inverseText contiene ora tutte le parole contenute precedentemente nel contenitore text, ma nell'ordine inverso.
l
L'invocazione inverseText.removeLast() restituisce l'ultima parola del contenitore inverseText, che e' anche la prima parola precedentemente contenuta nel contenitore text.
TextContainer.java
TextContainerTester.java
Ordinare studenti
Si consideri la classe Studente.java fornita dal docente.

Si scriva la classe StudentSortedContainer, che realizza l'interfaccia Container, per  memorizzare oggetti di classe Studente. L'interfaccia pubblica della classe è specificata in StudentSortedContainer.html.
I metodi add() devono inserire gli elementi nel contenitore ordinatamente secondo l'ordine naturale inverso della classe Studente (in ordine decrescente di matricola). Attenzione che il metodo add(String studenteString) genera l'eccezione java.util.NoSuchElementException se il parametro esplicito non e' nel formato corretto (nomexmatricola, dove x e' un carattere delimitatore).

Si renda eseguibile la classe, programmando il metodo main() in modo che legga da standard input i dati di un insieme di studenti, uno per riga (formato "nome:matricola") e inserisca gli studenti in un contenitore di classe SortedStudentContainer. Le righe che iniziano con il carattere '*' sono da considerarsi righe di commento. Se una riga non ha il formato corretto, si eviti di inserire lo studente nel contenitore e si mandi un messaggio a standard output del tipo:
Errore alla riga n. 9 >>> "testo della riga col formato errato"
Per realizzare tale funzionalita', catturare l'eccezione java.util.NoSuchElementException lanciata dal metodo add(String studenteString)  in caso di formato non corretto della stringa studenteString.

Si invii, successivamente, gli elementi del contenitore in ordine crescente a standard output.
Si provi la classe usando il file studenti.txt mediante redirezione dello standard input.
Esempio di uso: $java StudentSortedContainer < studenti.txt

Nota: se compilate la soluzione del docente, noterete che il compilatore non genera avvisi del tipo
Note: ComparableContainer.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

Il motivo della mancanza degli avvisi e' che la classe Studente.java usa l'interfaccia parametrica java.lang.Comparable<Studente> e non l'interfaccia semplice java.lang.Comparable.
StudentSortedContainer.java

Parte III
Linguaggio Java: ordinamento di oggetti generici comparabili
La mia soluzione
Ordinare generici oggetti comparabili
Si scriva la classe SortedContainer che realizza l'interfaccia Container.
La classe e' un contenitore generico in cui possono essere inseriti oggetti comparabili qualsiasi (la cui classe realizza l'interfaccia Comparable):  ad esempio, esemplari di classe java.lang.String, java.lang.Integer o di classe Studente.

L'interfaccia pubblica della classe e' riportata in SortedContainer.html.

Si scriva una classe di prova SortedContainerTester1.java che
- legge da standard input  i dati di un insieme di studenti (uno per riga nel formato "nome:matricola"). Se la riga inizia con il carattere '*' considerarla come una riga di commento
- costruisce degli oggetti di classe Studente e li  inserisce in un contenitore di classe SortedContainer
- invia successivamente a standard output  gli elementi contenuti nell'archivio in ordine decrescente. Per eseguire la funzione richiesta, si usi il metodo togliMax() che estrae dal contenitore l'elemento massimo (nel senso dell'ordinamento naturale della classe Studente).
Attenzione: il metodo togliMax ritorna un generico oggetto di classe Object!

Nota Bene:
Per memorizzare il riferimento restituito dal metodo removeMax() si puo' usare un riferimento di classe Studente con un forzamento esplicito:
Studente s = (Studente)archivio.removeMax();
Senza il forzamento esplicito, si deve usare un riferimento di classe Object:
Object obj = archivio.
removeMax();
Sul riferimento di classe s possiamo successivamente invocare i metodi della classe Studente, ad esempio:
int matr = s.matricola();
mentre il compilatore segnala errore se lo facciamo sul riferimento di classe Object perche' nella classe Object non e' definito il metodo matricola():
int matr = obj.matricola(); // ERRORE
Per la semantica del polimorfismo, funzionano correttamente invece i seguenti due enunciati:
System.out.println(s);
System.out.println(obj);
 
Uso: $java SortedContainer < inputFile

Come file di input si usi il file studenti.txt, mediante redirezione dello standard input.

Si codifichi, successivamente, la classe SortedContainerTester2.java che
- legge da standard input  le parole di un testo da standard input e le inserisce in un contenitore di classe SortedContainer.
- invia successivamente a standard output  gli elementi contenuti nell'archivio in ordine decrescente.
Si provi con il file animali.txt.
SortedContainer.java
SortedContainerTester1.java
SortedContainerTester2.java

Ordinare generici oggetti comparabili Si scriva la classe SortableContainer che realizza l'interfaccia Container.
Come la precedente, la classe e' un contenitore generico in cui possono essere inseriti oggetti che realizano l'interfaccia Comparable.

L'interfaccia pubblica della classe e' riportata in SortableContainer.html. La differenza rispetto al contenitore precedente e' che ora gli elementi non sono mantenuti ordinati nel contenitore, ma possono essere ordinati.

La classe realizza il metodo pubblico sort() per ordinare gli elementi comparabili contenuti nel contenitore. Per ordinare si usi l'algoritmo per fusione, programmandolo nella classe ArrayAlgorithms.
 
Si scriva la classe di prova SortableContainerTester.java che
- legge da standard input  i dati di un insieme di studenti (uno per riga nel formato "nome:matricola")
- costruisce degli oggetti di classe Studente e li  inserisce in un contenitire di classe SortableContainer
- invia successivamente a standard output  l'elenco degli elementi contenuti nell'archivio due volte, la prima volta senza ordinamento, la seconda volta dopo aver ordinato l'archivio.

Esempio usando i dati contenuti nel file studenti.txt:

*** ELENCO NON ORDINATO DI STUDENTI ***
56971:Giovanni Sette
56816:Franca Sei
56985:Dario Quattro
56993:Carlo Tre
56789:Bice Due
56799:Antonio Uno


*** ELENCO ORDINATO DI STUDENTI ***
56993:Carlo Tre
56985:Dario Quattro
56971:Giovanni Sette
56816:Franca Sei
56799:Antonio Uno
56789:Bice Due


Uso: $ java SortableContainerTester < inputFile

Come file di input si usi il file studenti.txt, mediante reindirezione dello standard input.
SortableContainer.java
SortableContainerTester.java
Ordinare oggetti confrontabili
Ecco un'altra versione delle classi dell'esercizio precedente: ContenitoreOrdinato.java e ContenitoreOrdinabile.java.
Le classi, fornite dal docente, si differenziano da quelle dell'esercizio precedente perche' sono definite per memorizzare generici oggetti di classe java.lang.Object. Naturalmente, per poter essere ordinati, gli oggetti inseriti nel contenitore devono essere di una classe che realizza l'interfaccia java.lang.Comparable.

1. Provare a compilare ed eseguire le seguenti classi di prova, fornite dal docente, che memorizzano oggetti di  classe MyComplex (che non realizza l'interfaccia java.lang.Comparable):

- ProvaContenitoreOrdinato1.java: usa la classe SortedContainer per memorizzare numeri complessi
- ProvaContenitoreOrdinato2.java: usa la classe ContenitoreOrdinato per memorizzare numeri complessi

- ProvaContenitoreOrdinabile1.java: usa la classe SortedContainer per memorizzare numeri complessi
- ProvaContenitoreOrdinabile2.java: usa la classe ContenitoreOrdinabile per memorizzare numeri complessi
- ProvaContenitoreOrdinabile3.java: usa la classe ContenitoreOrdinabile per memorizzare e ordinare numeri complessi

Uso: $ java ProvaContenitoreOrdinato1 < complex.txt

2. Rispondere alle seguenti domande:

2.1/ Perche' il compilatore segnala errore nelle classi ProvaContenitoreOrdinato1 e ProvaContenitoreOrdinabile1 e non nelle altre classi?
2.2/ Perche' in esecuzione ProvaContenitoreOrdinato2 e ProvaContenitoreOrdinabile3 provocano l'eccezione java.lang.ClassCastException?
2.3/ Perche' la classe ProvaContenitoreOrdinabile2 viene eseguita senza lanciare l'eccezione java.lang.ClassCastException?


Parte IV
Linguaggio Java: ordinamento di oggetti generici comparabili
La mia soluzione
Ordinare oggetti comparabili.
Criteri di ordinamento multipli. Definire Eccezioni.
Realizzare la classe Item.java che rappresenta un prodotto commerciale secondo l'interfaccia pubblica Item.html.
I campi che denotano un  prodotto commerciale siano:
- nome
- codice
- prezzo
- data di scadenza.
La classe realizza l'interfaccia parametrica java.lang.Comparable<Item> e permette di ordinare gli oggetti della classe alternativamente, per nome, codice, prezzo o data di scadenza.

Si definisca, inoltre, l'eccezione IllegalSortCriterionException.java, che viene lanciata dal metodo sortBy() della classe Item quando viene selezionato un criterio di ordinamento non valido.

Scrivere successivamente una classe di prova ItemTester.java che:
- legge da standard input  i dati di un insieme di prodotti. A ciascun prodotto sia riservata nel file una riga con il seguente formato: nome codice prezzo data_scadenza. Le righe che iniziano col carattere '*' siano considerate come righe di commento.
- inserisce i prodotti in oggetti di classe SortableContainer.java (definita in un esercizio precedente)
- ordina  contenitori, per nome, codice, prezzo e data di sacdenza.
- stampa a standard output la sequenza originale dei prodotti, i prodotti ordinati per nome, per codice, per prezzo e per data di scadenza.

Provare con il file di input prodotti.txt, mediante reindirezione dello standard input.

$java ItemTester < prodotti.txt

Esempio di risultato:

*** LISTA ORIGINALE DEI PRODOTTI ***
latte     : AB12      :        1.3  EURO scadenza 20061125 
uova      : CD11      :        0.9  EURO scadenza 20061129 
arance    : DA23      :        2.3  EURO scadenza 20061201 
tonno     : GE14      :       3.45  EURO scadenza 20061225 
maionese  : GH87      :        2.5  EURO scadenza 20070112 

*** LISTA DEI PRODOTTI IN ORDINE DI NOME ***
arance    : DA23      :        2.3  EURO scadenza 20061201 
latte     : AB12      :        1.3  EURO scadenza 20061125
maionese  : GH87      :        2.5  EURO scadenza 20070112 
tonno     : GE14      :       3.45  EURO scadenza 20061225 
uova      : CD11      :        0.9  EURO scadenza 20061129 

*** LISTA DEI PRODOTTI IN ORDINE DI CODICE ***
AB12      : latte     :        1.3  EURO scadenza 20061125 
CD11      : uova      :        0.9  EURO scadenza 20061129 
DA23      : arance    :        2.3  EURO scadenza 20061201 
GE14      : tonno     :       3.45  EURO scadenza 20061225 
GH87      : maionese  :        2.5  EURO scadenza 20070112 

*** LISTA DEI PRODOTTI IN ORDINE DI PREZZO ***
       0.9 EURO : uova      : CD11       scadenza 20061129 
       1.3 EURO : latte     : AB12       scadenza 20061125 
       2.3 EURO : arance    : DA23       scadenza 20061201 
       2.5 EURO : maionese  : GH87       scadenza 20070112 
      3.45 EURO : tonno     : GE14       scadenza 20061225 

*** LISTA DEI PRODOTTI IN ORDINE DI DATA DI SCADENZA ***
scadenza 20061125  : latte     : AB12      :        1.3  EURO
scadenza 20061129  : uova      : CD11      :        0.9  EURO
scadenza 20061201  : arance    : DA23      :        2.3  EURO
scadenza 20061225  : tonno     : GE14      :       3.45  EURO
scadenza 20070112  : maionese  : GH87      :        2.5  EURO

Item.java
ItemTester.java

IllegalSortCriterionException.java
Creare classi che realizzano l'interfaccia Comparable.
Usare l'ereditarieta'.
I poligoni convessi del piano sono caratterizzati dall'avere una misura del perimetro e dell'area. Questa proprieta' astratta e' descritta dalla seguente interfaccia  Polygon.java.

Nota: un'interfaccia puo' estendere un'altra interfaccia, come nel caso di Polygon che estende java.lang.Comparable. Le classi che realizzano l'interfaccia Polygon devono realizzare i metodi definiti in entrambe le interfacce, quindi area(), perimetro() e compareTo().

Gli oggetti di interfaccia poligono sono comparabili fra loro. Il criterio di comparazione sia la misura dell'area.

Usando la seguente classe immutabile MyPoint2D.java, fornita dal docente, che rappresenta un punto nel piano, realizzare le seguenti classi:
- MyPolygon che realizza l'interfaccia Polygon. La classe e' astratta perche' definisce ma non codificare il metodo area().
- MyTriangle che estende MyPolygon (rappresenta un triangolo)
- MyTetragon che estende MyPolygon (rappresenta un quadrilatero)
- MyRectangle che estende MyTetragon (rappresenta un rettangolo)
- MySquare che estende MyRectangle (rappresenta un quadrato).

Le interfacce pubbliche delle classi sono: MyPolygon.html, MyTriangle.html, MyTetragon.html, MyRectangle.html e MySquare.html.

Si scriva poi una classe PolygonTester.java che:
-  legga dal file poligoni.txt, mediante redirezione dello standard input, un insieme di poligoni, uno per riga; a ciascun poligono sia riservata una riga nel file. Nella riga siano riportati i vertici del poligono nel formato (x y).
- memorizzi i poligoni in un contenitre di classeSortedContainer del tipo realizzato nel primo esercizio
- stampi alla fine a standard output l'elenco ordinato dei poligoni.

Uso: $ java PolygonTester < inputfile
MyPolygon.java
MyTriangle.java
MyTetragon.java
MyRectangle.java
MySquare.java

PolygonTester.java


Parte V Linguaggio java e algoritmi di ordinamento
Una soluzione possibile
Ordinamento
Si scriva  un programma che

- legga da standard input una sequenza (di dimensione non predeterminata) di numeri interi, un numero per riga, senza alcun requisito di ordinamento (cioè, in generale, in ordine casuale);

- visualizza i numeri sullo standard output dopo aver eliminato eventuali valori duplicati (o, in generale, replicati più volte), lasciando quindi un solo esemplare di ciascun  numero presente nella sequenza originale; al termine, i numeri potranno trovarsi in un ordine diverso da quello iniziale (non è richiesto che siano ordinati).

Risolvere il problema in due modi

1. effettuando prima un ordinamento dell'insieme dei dati con l'algoritmo mergesort  (avente efficienza O(n lg n)), mettendo poi a punto e realizzando un algoritmo per l'eliminazione degli elementi replicati in un array ordinato con prestazioni asintotiche medie e di caso peggiore di tipo O(n)

2.   senza effettuare alcun ordinamento dell'insieme dei dati, mettendo a punto e realizzando un algoritmo con prestazioni asintotiche medie e di caso peggiore di tipo  O(n2).

Per la soluzione del problema dell'eliminazione dei duplicati, NON utilizzare array aggiuntivi oltre a quello in cui sono memorizzati i dati stessi (si possono usare altri array per la sola fase di ordinamento del punto 1.
provate da soli