University Logo

Università
di Padova

Diploma in Ingegneria Informatica
Corso di Fondamenti di Informatica 2
2 Moduli

A.A. 2001/2002

Versione 1.17 18/01/2002

Obiettivi ed esercizi di verifica

[1.05]Nota: poiché il materiale di una lezione viene studiato nei giorni successivi e sperimentato in laboratorio dopo la lezione successiva, se preferisce lo studente può ritardare la verifica del raggiungimento degli obiettivi nei giorni successivi alla prima delle due lezioni della settima successiva a quella cui quegli obiettivi si riferiscono (cioè a valle della sperimentazione in laboratorio). Si suggerisce comunque di provare ad affrontare il maggior numero possibile di esercizi qui proposti.


N.B. Le soluzioni in Java di alcuni degli esercizi di verifica si trovano nel package FI2.Examples.

Esercizi di verifica della settimana 1
Obiettivi:
Esercizi di verifica della settimana 2
Obiettivi:
Esercizi di verifica della settimana 3
Obiettivi:
Esercizi di verifica della settimana 4
Obiettivi:
Esercizi di verifica della settimana 5
Obiettivi:
Esercizi di verifica della settimana 6
Obiettivi:
Esercizi di verifica della settimana 7
Obiettivi:
Esercizi di verifica della settimana 8
Obiettivi:
Esercizi di verifica della settimana 9
Obiettivi:
Esercizi di verifica della settimana 10
Obiettivi:
Esercizi di verifica della settimana 11
Obiettivi:
Esercizi di verifica della settimana 12
Obiettivi:
Esercizi di verifica della settimana 13
Obiettivi:

Pagina principale Torna alla pagina principale


Esercizi settimana 1

Esercizio 1.1

Si progetti in UML un sistema di classi che descriva un anno di corso del diploma. Il sistema deve includere le seguenti entità:

Includere le relazioni che definiscono la frequenza di uno studente, l'insegnamento dei docenti, ipotizzando che un docente locale possa insegnare al massimo in due corsi, il materiale da sviluppare negli incontri-seminari, la valutazione degli studenti. Studiare i tipi di dati più opportuni per gli attributi.
Completare successivamente il progetto con alcuni metodi pubblici ragionevoli per il dominio del problema.
Trasformare il diagramma per passare all'implementazione Java e ricavare lo scheletro delle classi.

Esercizio 1.2

Realizzare una classe Bits che gestisce sequenze di 32 bit, singolarmente manipolabili, attraverso una rappresentazione interna con un valore intero. A tale scopo si implementino i seguenti metodi pubblici:

Esercizio 1.3

Definire una classe Square che memorizza quadrati di numeri interi. Prevedere nella classe:

Esercizio 1.4

Realizzare una classe Biglietto che rappresenta un biglietto di autobus urbano a tariffa unica. La classe include:

L'inizializzazione dei membri statici deve avvenire in un blocco statico. Definire successivamente la classe Carnet le cui istanze corrispondono ad un carnet di 10 biglietti venduti ad un prezzo scontato del 10%. Il carnet è memorizzato in un array interno alla classe. Definire i seguenti metodi:

Indice Torna all'indice


Esercizi settimana 2

Esercizio 2.1

Si realizzi la classe Cruci che rappresenta un cruciverba. La classe ha un costruttore che riceve i parametri n numero delle righe, m numero delle colonne, e un array di coppie di coordinate che rappresenta l'insieme delle caselle nere. Una coppia è rappresentata da un oggetto di classe Coppia, opportunamente definita. Cruci fornisce i seguenti servizi:

Esercizio 2.2

Si modifichi la classe Bits dell'esercizio 1.2 nella classe EBits in modo da renderla estendibile, e la si estenda con la classe SBits avente le seguenti caratteristiche:

Esercizio 2.3

Si definisca l'interfaccia SuFigura che rappresenta le azioni comuni su una figura geometrica, e precisamente i metodi:

Si definisca quindi una classe astratta Figura che implementa in parte SuFigura e che memorizza la posizione del centro della figura (oggetto di classe PointE) e realizza il solo metodo move.

Dalla classe Figura si derivino le classi Poligono e Cerchio. La classe Poligono memorizza il numero dei lati e i vertici, questi ultimi in un array di oggetti di classe PointE che esprimono le componenti cartesiane dei vettori che collegano il centro del poligono (un punto interno, mediano di una diagonale; si assuma il poligono convesso) e i vertici stessi; implementa inoltre tutti i metodi non ereditati (una versione semplificata show stampa su STDOUT le posizioni del centro e dei vertici) e il metodo perim che calcola il perimetro. La classe Cerchio memorizza il raggio del cerchio (il centro coincide con il centro geometrico) e implementa i metodi non ereditati (si noti che rot è in questo caso un'operazione nulla; una versione semplificata show stampa su STDOUT le posizioni del centro e il raggio) e i metodi perim che calcola il perimetro e area che calcola l'area.

Dalla classe Poligono si derivino le classi Rettangolo e Triangolo. La classe Rettangolo ridefinisce il metodo perim per tener conto dell'eguale lunghezza di due lati opposti, e implementa il metodo area. La classe Triangolo definisce il metodo area applicando la formula di Erone (area=sqrt(p*(p-a)*(p-b)*(p-c)) p semiperimetro).

Dalla classe Rettangolo si derivi la classe Quadrato che ridefinisce sia il metodo perim che area per tener conto della eguaglianza dei lati.

Esercizio 2.4

Si consideri la seguente interfaccia:

public interface FormattedOutput
{
    public String out(String format);
}
Il metodo out restituisce in una stringa una rappresentazione formattata del valore (principale) contenuto nell'oggetto che implementa l'interfaccia. La stringa format descrive le modalità di composizione della stringa di output secondo una convenzione derivata da quella utilizzata dalla funzione printf del linguaggio C e precisamente:

simbolo opzionale significato
- si allineamento a sinistra
numero si minimo numero di caratteri che forma la stringa di output:
se i caratteri significativi sono meno, vengono aggiunti spazi
a destra o a sinistra a seconda dell'allineamento
. [punto] si separa il numero precedente dal seguente
numero si nel caso di una stringa, è il massimo numero di caratteri da
rappresentare in output; nel caso di un numero, la precisione
(numero massimo di cifre per un intero, numero di decimali
per un floating point)
carattere di
conversione
no d intero decimale
o intero ottale: la stringa di output ha uno 0 iniziale obbligatorio
x intero esadecimale: la stringa di output ha un prefisso 0x obbligatorio
c singolo carattere
s stringa: o l'intera stringa oggetto oppure un suo prefisso lungo quanto la precisione, se specificata
f float rappresentato come [-]mmm.nnnn ove i decimali sono in numero pari alla precisione (6 se non indicata); per i complessi il formato è valido per ciascuna delle due componenenti ovvero [-]mmm.nnnn±mmm.nnnnj
e floating rappresentato come [-]m.nnnnnne[±]xx (decimali come sopra)

Tenendo presente che le classi wrapper di Java non sono estendibili, definire, con i metodi strettamente necessari, le seguenti classi wrapper che implementano l'interfaccia FormattedOutput:

In ciascuna classe il metodo out deve sollevare un'eccezione se la stringa di formato è illegale.

Definire infine infine la classe Format che implementa il metodo statico:

public static String out(String formatStr, FormattedOutput[] values);
che riceve nel parametro formatStr una stringa contenente sequenze di caratteri normali (compresi spazi) alternate a stringhe di formato precedute dal carattere speciale % (il carattere normale % viene rappresentato con %%). Ad esempio:
Il valore calcolato è: %3.5d (float=%e)
Nel parametro values il metodo riceve un array di oggetti FormattedOutput (cioè che implementano quell'interfaccia). Il metodo restituisce una stringa che è ricavata dalla stringa formatStr sostituendo nell'ordine ad ogni sottostringa di formato la stringa formattata prodotta dal metodo out applicato ai successivi oggetti dell'array values. Valutare cosa fare nel caso non vi sia una corrispondenza in numero tra sottostringhe di formato e oggetti nell'array (più strighe e meno oggetti, o viceversa). La classe Format contiene anche un metodo main di collaudo.

Indice Torna all'indice


Esercizi settimana 3

Esercizio 3.1

Definire un package Utility che include la classe Wrapper che ha 2 tipi di metodi statici:
Object wrap(basictype val);
basictype unwrap(Object obj, basictype dummy);
ove basetype è uno dei tipi base (boolean, char, byte, short, int, long, float, double). I due tipi di metodi sono overloaded, con una variante per ciascun tipo base.
Il metodo wrap incapsula in un oggetto, di una adeguata classe wrapper, il valore di tipo base passato come parametro e restituisce l'oggetto come valore di ritorno.
Un metodo unwrap estrae il valore da un oggetto di classe wrapper, purché compatibile, e lo restituisce come valore per quel particolare tipo base. Se l'oggetto wrapper non è quello esattamente corrispondente al tipo base, viene tentata se possibile una conversione, altrimenti viene sollevata l'eccezione java.lang.IllegalArgumentException. Il parametro aggiuntivo dummy consente di definire contratti diversi per le varianti overloaded.
Nel package Prove definire la classe ProvaWrapper il cui metodo main, in un ciclo, acquisisce da stdin un valore di uno dei tipi base, secondo la seguente convenzione:
tipo esempi
boolean false true
char 'c' '$' '\'' (l'apice) '\\' (la barra rovescia)
byte 12B
short -35S
int 24I 24
long -31L 27l
float .5f -2.4F 0.056f
double -2.1D .5D

Utilizzando la classe StreamTokenizer, nel ciclo viene identificato il tipo e successivamente chiamato il corrispondente metodo wrap con il valore acquisito, e l'oggetto tornato viene inserito in un oggetto di tipo java.util.Vector. Quando il vettore contiene 10 elementi, viene svuotato e i valori memorizzati vengono visualizzati, in ordine inverso a quello di inserimento, su stdout.

Esercizio 3.2

Estendere la classe BufferedReader per operare un filtraggio su file in formato HTML atto ad eliminare i tag e i commenti, ovvero tutte le sequenze del tipo:
<...<...<...>...>...> (bilanciate)
<!--...........-->
Es:

<H3>Testo 1<!--Testo    2<BR><P>
Testo 3 --> Testo 4
Testo 5<BR>Testo 6
viene trasformato in:
Testo 1 Testo 4
Testo 5Testo 6
Prevedere un metodo main di collaudo.
(Suggerimento: si tenga un conteggio delle parentesi aperte e chiuse dei tag).

Esercizio 3.3

Si realizzi la classe IntegerConvert che definisce 2 metodi statici:
public static void str2Int(Reader in, DataOutputStream out)
throws ....
public static void int2Str(DataInputStream in, Writer out);
throws ....
Il primo metodo riceve in input un testo costituito da numeri esadecimali a massimo 8 cifre: le cifre sono rappresentate nello stream da caratteri ASCII a 7 bit, 1 byte per carattere, in campi da 8 byte. Se un numero è rappresentato da meno di 8 cifre, vengono aggiunti caratteri spazio a destra del numero a completare il campo. Il valore rappresentato va convertito in intero: a questo scopo si applichi la formula base della notazione posizionale in forma fattorizzata, che per un numero positivo (<= $7fffffff) è data da:
a = a7*167 + a6*166 + ... + a1*16 + a0 =
((...(a7*16 + a6) *16 + a5)*16 + ... +
a1)*16 + a0
che ripete l'operazione elementare:
(pi << 4) + ai
(ricavare un'analoga espressione per i numeri negativi).

I valori convertiti vanno salvati nello stream di output nella rappresentazione interna. L'altro metodo effettua l'operazione opposta. Scrivere un metodo main di collaudo che effettua letture e scritture su file.

Esercizio 3.4

Si realizzi la classe Rombo che memorizza in un array a 4 componenti le coordinate dei vertici di un rombo, rappresentate da oggetti di classe Point. La classe Rombo, oltre ad includere il costruttore di default che inizializza l'oggetto a rappresentare il quadrato di lato unitario, con un vertice nell'origine e 2 lati sui 2 semiassi cartesiani positivi, include un costruttore che riceve 4 oggetti Point che rappresentano i 4 vertici, e uno che riceve in una stringa il nome di un file e ricava le coordinate dei 4 vertici dal contenuto del file indicato. In quest'ultimo caso, la lettura dal file avviene utilizzando un oggetto di classe PointInputStream: questa classe estende la classe DataInputStream con costruttori analoghi a quest'ultima e con in più il metodo readPoint che restituisce un nuovo oggetto Point leggendone le due coordinate mediante i metodi della classe DataInputStream da cui eredita. La classe Rombo include inoltre il metodo toString, che ha l'usuale significato, e un metodo vertici che restituisce, come ObjectIterator, un oggetto di classe RomboEnum, definita come classe locale al metodo vertici. Tale oggetto realizza l'iterazione dei vertici del rombo cui è associato assumendo che l'oggetto che rappresenta quest'ultimo sia immutabile (una volta inizializzato non si può modificare ma solo leggere). Si aggiunga alla classe Rombo un metodo main di collaudo.

Indice Torna all'indice


Esercizi settimana 4

Esercizio 4.1

Realizzare una classe OrderedStrings che mantiene in un array un insieme di n stringhe prelevate da un file (una stringa per riga), ove si assumono già ordinate sulla base dei primi 3 caratteri della prima parola e dei primi 3 della seconda; l'ordinamento tra caratteri nella stessa posizione, e nei casi di non egual numero di caratteri coinvolti, è l'usuale (confronto lessicografico). Si preveda nella classe un metodo di ricerca di una stringa che utilizzi una ricerca binaria e un metodo main di collaudo. Per i confronti si pensi di implementare opportunamente l'interfaccia FI2.Util.Comparable.

Esercizio 4.2

Si realizzi con la classe Interp un semplice interprete di una macchina a stack. La macchina dispone di uno stack e di un insieme di NVAR variabili, entrambi per valori floating point in doppia precisione. Le primitive della macchina sono le seguenti:
pushi val push del valore val
pushv i push del contenuto della variabile i (0..i..NVAR-1)
popv i pop dallo stack alla variabile i
add pop+pop -> push
sub -(pop-pop) -> push
mul pop*pop -> push
div 1/(pop/pop) -> push
printv i output su STDOUT del contenuto della variabile i
prints output su STDOUT del top dello stack (senza rimozione)
exit Uscita dall'interprete
Nel realizzare le primitive algebriche si tenga conto dell'ordine degli operandi (come esplicitato nella spiegazione per sub e div).
Il metodo intp attiva l'interprete acquisendo i comandi da STDIN, uno per riga e gestendo opportunamente i casi di stack vuoto e pieno. Sfruttando le caratteristiche della classe StremTokenizer, prevedere la possibilià di inserire commenti di linea che iniziano con il carattere #. Aggiungere il metodo main di collaudo.

Esercizio 4.3

Implementare con la classe StringStack l'interfaccia Stack in modo da far uso di uno StringBuffer per memorizzare in forma LIFO elementi costituiti da singoli caratteri. Aggiungere nella classe implementata un metodo print che effettui su STDOUT un output 'significativo' dello stack (mettendo, in particolare, in evidenza il top dello stack). e un metodo di svuotamento dello stack. Realizzare inoltre un metodo statico String reverse(String s) che restituisce la versione speculare della stringa parametro, utilizzando un oggetto di classe StringStack. Aggiungere un metodo main di collaudo.

Esercizio 4.4

Si voglia realizzare un metodo di verifica ed evidenziazione del bilanciamento di parentesi rotonde in un'espressione generica. A tale scopo si realizzi nella classe CheckBalance il metodo ricorsivo:
public static void check()
che nel campo statico di classe expr riceve, come stringa, l'espressione da esaminare, nel campo startix la posizione nella riga da cui iniziare la verifica, e che usa uno stack di interi. Il metodo effettua un ciclo entro il quale, a partire dalla posizione startix, ricerca la prima '(' non preceduta da ')': se la trova, salva la posizione della parentesi aperta sullo stack, avanza startix alla posizione che segue immediatamente quella della parentesi e si richiama ricorsivamente; al ritorno, effettua un pop dallo stack della posizione salvata e, se startix 'punta' ad una parentesi chiusa, stampa su STDOUT le posizioni relative alla coppia di parantesi (per quella aperta, ricavata dallo stack, per quella chiusa, da startix), avanza quindi di una posizione startix e ricicla. Se dal ritorno della chiamata ricorsiva startix è posizionato oltre la fine della stringa, il metodo termina con una segnalazione di errore di parentesi aperta non bilanciata. Dal ciclo si esce se non vengono trovate altre parentesi aperte o se si trova una parentesi chiusa prima di una aperta e la chiamata non corrisponde al primo livello di ricorsione: in quest'ultimo caso il metodo, prima di terminare, avanza startix in modo da 'puntare' alla parentesi chiusa. Se il livello di ricorsione è il primo, il ciclo prosegue dopo una segnalazione di parentesi chiusa non bilanciata e l'avanzamento di una posizione di startix.
Il metodo:
public static void runCheck(String ex)
avvia il procedimento impostando la stringa-espressione (ricevuta nel parametro ex) e il valore iniziale di startix.
Aggiungere un metodo main di collaudo che richieda da STDIN, una dopo l'altra, le espressioni da verificare.

Esercizio 4.5

Si implementi l'interfaccia Queue con la classe TableQueue che utilizza una particolare struttura di dati descritta nel seguito.
Si definisca la classe TbNode con 2 campi, un Object che rappresenta l'elemento inserito nella struttura di dati, e un indice (next) che rappresenta la posizione, in un array QTab di TbNode, del nodo successore. L'array-tabella QTab è composta da NELEM nodi ed è inizializzata in modo che tutti i nodi, non associati ad elementi, siano collegati in forma di stack a partire dal nodo di posizione 0. In ogni istante la posizione del top dello stack è rappresentata dal campo top in TableQueue. Con i nodi prelevabili dallo stack viene costituita una coda in forma concatenata, per la quale il campo head fornisce, in ogni istante, la posizione del primo elemento e il campo tail quella dell'ultimo. Se la coda è vuota entrambi i campi valgono NULLPOS (pari a -1). L'inserimento di un elemento in coda corrisponde ad estrarre un nodo dallo stack, associarlo all'elemento e inserirlo in fondo alla coda, diventandone il nuovo nodo terminale. L'estrazione corrisponde, se la coda non è vuota, alla rimozione del nodo in testa alla coda e alla sua restituzione allo stack dei nodi liberi. Aggiungere un metodo main di collaudo.

Indice Torna all'indice


Esercizi settimana 5

Esercizio 5.1

Si implementi l'interfaccia Queue con la classe FileQueue che realizza in forma circolare su file una coda di dimensioni massime prefissate (MAXELEM). Ogni elemento della coda è rappresentato da un blocco di 256 byte. Il costruttore FileQueue(String name), se il file name non esiste, lo crea e lo inizializza a coda vuota. L'inserimento di un oggetto corrisponde all'inserimento nella coda sul file di un byte che rappresenta una lunghezza (0..255), seguito da una stringa di caratteri ASCII a 7 bit che rappresenta la stringa ritornata dalla chiamata toString applicata all'oggetto passato come parametro, troncata ai primi 255 caratteri se più lunga di tale quantità. L'estrazione ritorna un oggetto stringa con il contenuto dell'elemento (tenuto conto del valore di lunghezza associato).
Utilizzare la classe FileQueue per realizzare una forma semplificata di spooling di file di testo su STDOUT. Si realizzi a tale scopo una classe Sp con un metodo main che inserisce in un FileQueue di nome prefissato il nome del file da visualizzare, passato come parametro sulla linea di comando. Si realizzi inoltre la classe Spool il cui metodo main in un ciclo effettui l'estrazione dal FileQueue usato da Sp di ciascuno dei singoli nomi di file presenti, visualizzandone poi l'intero contenuto su STDOUT attraverso le funzioni dell'oggetto FI2.Util.FI2Sys. Nel ciclo è prevista una sospensione per 20 s prima di visualizzare il successivo, finché la coda si svuota, cosa che fa terminare il metodo main. Per l'attesa si utilizzi il metodo Thread.currentThread.sleep(long milliseconds). Si trascurino problemi di interferenza tra Sp e Spool, assumendo che i loro metodi main non operino mai in concorrenza.

Esercizio 5.2

Applicando la tecnica dell'adattatore, utilizzare la classe LinkedQueue per realizzare la classe PointQueue che memorizza in una coda esclusivamente oggetti di tipo Point. Gli oggetti rappresentano i vertici di una spezzata sul piano: definire una classe Spezzata che, nel suo metodo main, riceve da STDIN le coordinate di una sequenza di punti, li memorizza temporaneamente in una coda di tipo PointQueue e, su sollecitazione dell'input, li estrae tutti e li copia in un array di dimensioni opportune, verificando se la spezzata ha almeno due segmenti che si intersecano e fornendo la risposta su STDOUT, prima di ricominciare l'acquisizione di una nuova sequenza di punti.

Esercizio 5.3

Si consideri la seguente classe:

public class ContainerHelper
{
    public static final int SETEMPTYCOM = 0;
    public static final int SETDOUBLECOM = 1;
      // codici comandi di attivazione
    private ContainerActivator act;

    /**[c]
     * costruttore base
     * @param a  attivatore da associare
     */
    public ContainerHelper (ContainerActivator a)
    {  act = a; }

    /**[m]
     * metodo di impostazione dell'attivatore
     * @param a  attivatore da associare
     */
    public void setActivator(ContainerActivator a)
    {  act = a; }

    /**[m]
     * metodo di attivazione dell'attivatore
     * @param command  indice del metodo da attivare
     * @return Container  nuovo contenitore dopo l'operazione
     * attivata (coincide con il contenitore originario
     * se con approccio ad oggetti mutabili)
     */
    public Container activate(int command)
    {
        switch(command)
        {
          case SETEMPTYCOM:
            return act.setEmpty();
          case SETDOUBLECOM:
            return act.setDouble();
          default:
            return null;
        } //switch
    } //[m] activate

} //{c} ContainerHelper

Gli oggetti di classe ContainerHelper consentono di attivare in modo indiretto i metodi di oggetti che implementano la seguente interfaccia:
public interface ContainerActivator
{

    /**[m][a]
     * svuotamento del contenitore
     * @return Container  il contenitore stesso se
     * oggetto mutabile, un nuovo contenitore vuoto
     * se immutabile
     */
    public Container setEmpty();

    /**[m][a]
     * raddoppio degli elementi del contenitore -
     * i riferimenti di quelli presenti vengono
     * copiati se con approccio non mutabile
     * @return Container  il contenitore stesso se
     * oggetto mutabile, un nuovo contenitore vuoto
     * se immutabile
     */
    public Container setDouble();

} //{c} ContainerActivator
Realizzare la classe LisArrayStack che estende la classe FI2.Linear.ArrayStack in modo da implementare l'interfaccia FI2.Set.Container e includere un attivatore di classe ContainerHelper. Quest'ultimo viene inizializzato in fase di costruzione come unico oggetto di una classe anonima (definita quindi nel costruttore di LisArrayStack) che implementa opportunamente i metodi dell'interfaccia ContainerActivator. Nel realizzare l'implementazione di Container si tenga presente ai limiti posti dalla classe ArrayStack.

Esercizio 5.4

Si estenda la classe FI2.Linear.ArrayQueue al fine di implementare l'interfaccia Deque. Se necessario partire da una versione modificata ArrayQueueE più adatta ad una successiva estensione.

Esercizio 5.5

Con la tecnica dell'adattatore applicata alla classe FI2.Linear.ArrayQueue, definire la classe TempQueue dotata dei seguenti metodi (oltre ai costruttori):

Aggiungere un metodo main di collaudo che riceve da STDIN gli elementi da inserire e, su comando speciale, effettua la visualizzazione degli elementi in coda e lo svuotamento.

Esercizio 5.6

Con la tecnica dell'esercizio 4.5 (TableQueue) realizzare un'implementazione di List: derivare il metodo di collaudo da quello di ArrayList con i minimi cambiamenti e collaudare la classe con bubble-sort.

Esercizio 5.7

Si voglia realizzare in forma concatenata una rappresentazione di un linguaggio a liste innestabili (si pensi al LISP o al LOGO): l'input è costituito da frasi in cui sono identificabili parole e numeri inseriti in parentesi rotonde innestabili. Ad esempio:
aldo bruno (carlo ((dino 35 elmo) franco(giulio ()ileo) 12)) lucio
Ogni elemento nella frase è rappresentato da un nodo di una struttura concatenata. Il nodo ha 2 riferimenti: una sottolista (subl) e il nodo successore (succ). Se un elemento è atomico (parola o numero) è rappresentato da un nodo in cui il riferimento subl è nullo; se l'elemento è una parentesi aperta, il campo succ riferisce il nodo che rappresenta l'elemento che segue, nella frase, la corrispondente parentesi chiusa, mentre il campo subl riferisce il primo elemento contenuto nella sottolista aperta dalla parentesi e che inizia una successione di nodi che termina con il nodo che rappresenta la corrispondente parentesi chiusa; se l'elemento è una parentesi chiusa, il campo succ, come per un elemento atomico, riferisce il nodo seguente nella frase. Per la frase sopra si avrebbe:


sistemare la figura
+----+-+-+   +-----+-+-+   +--+-+-+   +-----+-+-+
|aldo|^| |-- >|bruno|^| |-- >|(1| | |-- >|lucio|^|^|
+----+-+-+   +-----+-+-+   +--+-+-+   +-----+-+-+
                               |         ^
  +----------------------------+         |
  |                                      |
  |                            +---------+
  v                            |
+-----+-+-+   +--+-+-+   +--+-+-+
|carlo|^| |-- >|(2| | |-- >|)1|^| |
+-----+-+-+   +--+-+-+   +--+-+-+
                  |        ^
  +---------------+        |
  |                        +--------------------------+
  v                                                   |
+--+-+-+   +------+-+-+   +--+-+-+   +--+-+-+   +--+-+-+
|(3| | |-- >|franco|^| |-- >|(4| | |-- >|12|^| |-- >|)2|^| |
+--+-+-+   +------+-+-+   +--+-+-+   +--+-+-+   +--+-+-+
    |          ^              |        ^
    |          |              |        |
    |          |              |        +-----------------------------------------------------+
  +-+          |              +-------------------+                                          |
  |            +---------------------------+      |                                          |
  v                                        |      v                                          |
+----+-+-+   +--+-+-+   +----+-+-+   +--+-+-+   +------+-+-+   +--+-+-+   +----+-+-+   +--+-+-+
|dino|^| |-- >|35|^| |-- >|elmo|^| |-- >|)3|^| |   |giulio|^| |-- >|(5| | |-- >|ileo|^| |-- >|)4|^| |
+----+-+-+   +--+-+-+   +----+-+-+   +--+-+-+   +------+-+-+   +--+-+-+   +----+-+-+   +--+-+-+
                                                                   |         ^
                                                                   |         |
                                                                   | +-------+
                                                                   v |
                                                               +--+-+-+
                                                               |)5|^| |
                                                               +--+-+-+
(Nella figura le parentesi sono state corredate dal numero di livello)
Definire la classe PLNode che rappresenta il nodo della lista e la classe ParList che rappresenta la struttura di dati 'lista con parentesi'. Prevendere in quest'ultima classe: In sintesi, la costruzione della lista avviene in ordine, elemento per elemento, secondo l'ordine di input (si tenga conto del mantenimento delle posizioni intermedie rappresentato dalle parentesi aperte per poter tornare indietro di un livello in occasione di una parentesi chiusa); si dispone poi di un meccanismo di navigazione, via enumeratore, che restituisce gli elementi nello stesso ordine di creazione, e di due metodi first e butFirst che restituiscono un ParList clonato che è una porzione della lista originaria, rispettivamente il primo elemento atomico (o la prima sottolista) e tutto il resto. Il metodo di collaudo, per l'esempio sopra, produrrebbe l'output:
aldo bruno
----carlo
--------
------------dino 35 elmo
--------franco
------------giulio
----------------
------------ileo
--------12
----
lucio
(Si tratta di un esercizio piuttosto elaborato ma istruttivo).

Indice Torna all'indice


Esercizi settimana 6

Esercizio 6.1

Estendere la classe ArrayList con la classe IAPS in modo che includa il metodo remElements che restituisce un oggetto di classe APSIter che implementa l'interfaccia java.util.Iterator di Java 1.2. È necessario apportare modifiche nella classe ArrayList per definire la classe APSIter come classe interna ad IAPS?

Esercizio 6.2

Adattare l'esempio di bubble-sort in modo da ordinare oggetti di classe java.awt.Color, che rappresenta un colore a 24 bit, sulla base della luminosità (brightness) del colore (si veda per questo la rappresentazione HSB del colore).

Esercizio 6.3

Realizzare un programma in grado di interpretare un testo che contiene comandi per la creazione di oggetti grafici in una finestra. La sintassi prevede che il testo d'ingresso, un comando per riga, possa includere i seguenti comandi:

Comando Significato
B <stringa> bottone con etichetta
L <stringa> label
F <stringa> text field con valore iniziale
T <stringa> text area con valore iniziale
C <stringa> checkbox con valore
Per ogni comando nel testo d'ingresso il programma deve aggiungere nella finestra il relativo componente grafico ed un ascoltatore predefinito.

Esercizio 6.4

Realizzare un programma con un'interfaccia grafica dotata di 4 bottoni etichettati come 'cerchio', 'rettangolo', 'rombo', 'triangolo'. La pressione su uno dei bottoni aggiunge, in un'area grafica sottostante, un corrispondente oggetto geometrico in posizione e dimensioni casuali (in un range stabilito). Se si preme il mouse all'interno dell'area dell'oggetto geometrico, questo viene selezionato e deselezionato, e si può spostare con il mouse con un'azione di dragging. La memorizzazione dei descrittori degli oggetti geometrici deve avvenire in una Sequence.

(Suggerimenti: definire in un'interfaccia DrawMovable i metodi che gli oggetti grafici devono implementare (draw per il disegno, move per la traslazione, contains per verificare se un punto è all'interno di una figura. Ove possibile, usare la classe PointE. Inserire in una classe astratta SelFigure, che gli oggetti grafici estendono, la memorizzazione dello stato di 'selezionato'. Il metodo draw disegna, per ogni oggetto grafico, se stesso in un colore che dipende dalla selezione. Per il rombo e per il triangolo sfruttare le caratteristiche della classe Polygon di Java. L'area grafica è un (derivato di un) Canvas su cui sono registrati gli ascoltatori per il mouse. Il metodo paint chiama il metodo draw di tutti gli oggetti nella sequenza.)

Esercizio 6.5

Realizzare un oggetto LevelBar che rappresenta una barra di livello per un misuratore generico. Prevedere un metodo di aggiornamento del valore rappresentato che consenta di precisare anche una descrizione dell'unità di misura e il range dei valori ammessi, in modo che la barra faccia vedere il valore attuale anche mediante un numero di percentuale (rispetto al fondoscala).

Indice Torna all'indice


Esercizi settimana 7

Esercizio 7.1

Pensare di rappresentare un filesystem che definisce una gerarchia di directory sottoforma di albero a più livelli. Definire, in pseudocodice, le visite che realizzano:
  1. la lista dei file di un certo directory
  2. la rappresentazione a parentesi di livello dei directory del filesystem (senza file)
  3. la lista dei primi file di tutti i directory ad un certo livello

Indice Torna all'indice


Esercizi settimana 8

Esercizio 8.1

Rappresentare con un albero binario lo split di finestre sullo schermo. A partire da una finestra che occupa tutto lo schermo, ogni finestra può essere suddivisa in due e le due finestre che si ottengono vanno ad occupare complessivamente l'area della finestra genitrice. Si veda in figura la corrispondenza tra un layout e il relativo albero.

+------------------+
|a-a1              |
|                  |
+---------+--------+    a (a1 a2 (b1 b2 (c1 c2)))
|a2-b1    |b2-c1   |
|         |        |
|         +--------+
|         |c2      |
|         |        |
+---------+--------+
Prevedere primitive di split, fuse (il contrario di split), di rappresentazione grafica (in scala) del layout delle finestre.

Esercizio 8.2

Utilizzando la tecnica figlio-fratello per la rappresentazione di alberi generici con alberi binari, costruire l'immagine di un directory, di pathname fornito come parametro, collocando come elementi dati dell'albero oggetti di classe java.io.File. Realizzare il metodo showDir che, fornito un nodo dell'albero che rappresenta un directory, visualizzi il listato del contenuto di quest'ultimo (come il comando DIR del DOS ma in modo semplificato).

Esercizio 8.3

Implementare l'interfaccia ExpandBinaryTree con il supporto di un array: i nodi dell'albero contengono 3 indici che rappresentano le posizioni dei nodi figli nell'array e quella del padre. L'array è inizializzato come uno stack (concatenato) di nodi liberi; ogni volta che un nuovo nodo viene inserito, viene prelevato dallo stack e ad esso restituito quando viene rimosso dall'albero.

Indice Torna all'indice


Esercizi settimana 9

Esercizio 9.1

Definire un'implementazione dell'interfaccia Comparator per confrontare oggetti di classe java.util.Date. Utilizzare questo comparatore per eseguire un selection sort di n valori di classe Date.

Esercizio 9.2

Realizzare uno scheduler temporale che riceve da stdin l'indicazione di un evento rappresentato da un istante temporale (espresso in qualche modo coerente) e una stringa descrittiva. Lo scheduler è costituito da due thread: uno riceve in un ciclo gli eventi da stdin e li inserisce in una CdP in ordine temporale crescente, l'altro thread in un ciclo valita a cadenza temporale fissa T se l'istante inferiore indicato nella CdP è scaduto e, in caso affermativo, lo estrae dalla coda e ne fornisce segnalazione su stdout.

Esercizio 9.3

La classe DescTable include un vettore (descs) di n oggetti di classe Desc, preallocati nel costruttore di base, e una coda a priorità inizialmente vuota. La classe Desc rappresenta un descrittore includente un intero di identificazione (pid), un intero che rappresenta una priorità (prio), e una variabile di stato (state) che può assumere uno dei valori UNDEFINED, CREATED, ACTIVATED, SUSPENDED, TOBEKILLED, KILLED. Nel vettore descs gli n descrittori vengono inizializzati con lo stato UNDEFINED e con pid pari all'indice del corrispondente elemento del vettore. La classe DescTable realizza i seguenti metodi:

public int create(int pr) throws FullException;
Cerca il primo descrittore libero (stato UNDEFINED o KILLED) e, se lo trova, lo alloca (trasformando lo stato in CREATED), vi assegna la priorità pr e ne restituisce il pid. Se tutti i descrittori sono allocati, solleva l'eccezione indicata.

public void activate(int pid) throws InvalidArgumentException
Se lo stato del descrittore identificato da pid % n è CREATED o SUSPENDED, inserisce il descrittore nella CdP usando la priorità in modo da ordinare i descrittori per priorità decrescente; in caso contrario solleva l'eccezione indicata.

public int suspend() throws EmptyException
Estrae il descrittore di massima priorità dalla CdP. Se il suo stato è TOBEKILLED, lo trasforma in KILLED, incrementa del valore n il suo pid e restituisce il valore NOPID = -1. Altrimenti (lo stato deve essere in questo caso ACTIVATED), trasforma lo stato in SUSPENDED e restituisce il valore del pid. Se invece la coda è vuota, solleva l'eccezione indicata.

public void kill(int pid) throws InvalidArgumentException
Se lo stato del descrittore identificato da pid % n è UNDEFINED o KILLED, solleva l'eccezione indicata. Altrimenti se lo stato è ACTIVATED, estrae il descrittore dalla CdP (ove è certamente presente, sebbene in generale non con la massima priorità). Dopo questa estrazione oppure (senza estrazione) se lo stato era CREATED, lo trasforma in KILLED e incrementa del valore n il suo pid. Se lo stato era originariamente SUSPENDED, lo trasforma in TOBEKILLED.

Si noti che l'incremento di pid fa in modo che lo stesso descrittore abbia pid distinti se viene osservato in due momenti diversi in entrambi i quali è nello stato CREATED , ACTIVATED o SUSPENDED ma avendo subito tra questi due istanti un kill e un nuovo create. Dal pid si può comunque ricavare l'indice dell'elemento nel vettore attraverso l'operazione di modulo (pid % n).

Aggiungere un metodo main di controllo che, allocata una tabella di classe DescTable di n=10 elementi, ne crea e attiva 6 e poi esegue 20 volte un ciclo con cadenza temporale prefissata: ad ogni ciclo decide a caso di useguire uno dei 4 metodi. Se viene eseguito un suspend, il pid restituito viene inserito in una coda FIFO; se viene eseguito un activate, il pid del descrittore da attivare viene estratto dalla testa della coda FIFO; se viene eseguito un kill, viene scelto un pid a caso tra i 6 attivati, gestendo l'eventuale eccezione.

Indice Torna all'indice


Esercizi settimana 10

Esercizio 10.1

Realizzare un programma che, leggendo un sorgente Java (assunto privo di commenti), preveda di contare le occorrenze dei vari simboli presenti nel sorgente (comprese le parole chiave del linguaggio). Il conteggio viene mantenuto, per ciascun simbolo, in un oggetto di classe Symbol. Gli oggetti di questa classe vengono creati e collocati in un dizionario in ordine lessicografico di sombolo.
Mentre la letttura dal file sorgente e il popolamento dell'archivio con i simboli letti avviene in un costruttore, si prevedono metodi per i seguenti scopi:

Esercizio 10.2

Risolvere l'esercizio 10.1 con un dizionario basato su tabella hash. Nei metodi che richiedono l'ordinamento, introdurre come supporto una CdP basata su heap.

Esercizio 10.3

Costruire un dizionario Figures, basato su tabella hash, i cui elementi sono i descrittori dei poligoni dell'esercizio 6.4. La chiave (completa) è rappresentata dal tipo di figura assieme ai parametri geometrici (grandezza e posizione). Studiare un hashing adeguato al dominio delle chiavi. Includere nella classe Figures un metodo che fornisca una enumerazione delle figure copiando i riferimenti agli oggetti descrittori in una sequenza ordinata in base alla chiave (definendo un opportuno comparatore) e utilizzando l'algoritmo di heap sort.

Esercizio 10.4

Si implementi l'interfaccia Dictionary con la classe BucketDict che usa come supporto un bucket array di N elementi, sapendo che le chiavi sono numeri interi nel range 0..N-1. Ogni elemento dell'array è di tipo Object; una collisione di chiave deve sollevare un'eccezione.
Realizzare inoltre un'applicazione con la classe Presenze che simula una apparecchiatura di controllo delle presenze, memorizzando nei 2 bucket array di due oggetti di tipo BucketDict i descrittori dei dipendenti rispettivamente assenti e presenti. Ogni descrittore è costituito da un oggetto di classe Cartellino che include Cognome, Nome, il codice univoco del dipendente (numero 0..N-1 che è anche la chiave), un array i cui elementi, uno per ogni giorno del mese corrente, sono rappresentati da una sequenza di max. 6 orari. Il metodo void entra(int key) 'sposta' il cartellino, identificato dalla chiave parametro, dagli assenti ai presenti, aggiungendo alla lista di orari associata al cartellino l'ora corrente; Il metodo void esce(int key) esegue una operazione analogo ma da presenti ad assenti. Gestire opportunamente le situazioni eccezionali. Si assuma che in fase di costruzione di un oggetto Presenze vengano caricati come assenti un determinato numero di dipendenti.

Esercizio 10.5

Realizzare un address book di indirizzi di posta elettronica utilizzando un dizionario implemenetato con un albero binario di ricerca. In fase di costruzione l'archivio viene letto da un file (testuale, con un formato opportunamente definito, es: , , ) e, in dipendenza del valore di un parametro, la memorizzazione avvenga utilizzando come chiave la concatenazione di cognome e nome, oppure l'indirizzo di email. Prevedere primitive di ricerca, cancellazione, lettura e salvataggio su file nel formato previsto.

Esercizio 10.6

Implementare la variante random dell'algoritmo di quick sort utilizzando i metodi dell'interfaccia Sequence.

Indice Torna all'indice


Esercizi settimana 11

Esercizio 11.1

Implementare un metodo statico indexes che riceve come parametri due stringhe str1 e str2 e che restituisce come valore di ritorno, in un array di interi, le posizioni nella stringa str1 in cui è presente la stringa str2, utilizzando l'algoritmo KMP. Se non vi sono occorrenze di str2 in str1, il metodo ritorna null.

Indice Torna all'indice


Pagina principale Torna alla pagina principale