Quelli che seguono sono testi d'esame creati dal Prof. Lanfranco Lopriore per il corso di Fondamenti di Informatica del 1º anno del corso di laurea in Ingegneria Informatica presso l'Università di Pisa durante l'anno accademico 1997-98. Per ognuno di essi è proposta una soluzione sotto forma di file sorgente C++. Le soluzioni sono state scritte da me, Francesco Potortì, e sono state compilate utilizzando sia il compilatore Visual C++ della Microsoft, sia il compilatore g++ sviluppato nell'ambito del progetto GNU.
I file usano il suffisso .cc
, che è lo standard per GNU
make. Può esser comodo usare questo makefile
per compilare i sorgenti. GNU make per ambienti Microsoft può essere
trovato a http://www.delorie.com/djgpp/,
assieme ad altri programmi GNU. In alternativa, per far sì che il
Visual C++ riconosca automaticamente i file come sorgenti C++, conviene
cambiare il loro suffisso in .cpp
.
Nelle prime stesure dello standard, la new
poteva
ritornare un valore 0, che indicava fallimento, per cui risultava
necessario controllare ogni valore di ritorno, o meglio utilizzare la
set_new_handler
. Nelle soluzioni proposte suppongo di
utilizzare un compilatore non obsoleto che, come previsto dalla
versione definitiva dello standard, generi un'eccezione quando la
new
fallisce. Non utilizzo la
set_new_handler
per facilitare la portabilità al Visual
C++ che, ignorando lo standard, non la implementa, o meglio ne
implementa solo uno stub non funzionale, che compila ma genera
un errore all'esecuzione.
La soluzione del primo esercizio può essere compilata
per produrre un file eseguibile. Benché il testo richieda solo
l'implementazione del tipo di dati, per effettuare un controllo
completo della correttezza del codice e rendersi pienamente conto del
suo funzionamento è necessario scrivere un programmino che implementi
un main
, il quale utilizzi le classi implementate. Tutte
le altre soluzioni comprendono un singolo file contenente solo le
dichiarazioni e definizioni richieste dalle tracce di esame, e pertanto
si possono compilare in un file oggetto che non può girare. Per
ottenerne dei programmi eseguibili, bisognerà scrivere un
main
sul modello del primo esercizio.
La maniera migliore di utilizzare il materiale qui contenuto consiste nel cercare di eseguire le tracce senza prima leggere le soluzioni proposte. Prima di scrivere anche una sola riga di codice, è importante aver letto attentamente e con calma il testo per almeno due volte. La cosa più importante per una corretta soluzione è la scelta delle strutture dati che saranno utilizzate. Una scelta oculata consente di scrivere la minima quantità di codice, e di ridurre la possibilità di errori.
Le soluzioni che propongo sono state esaminate accuratamente, ma naturalmente qualche errore può essermi sfuggito. Prego chi se ne dovesse accorgere di segnalarmelo, scrivendomi un messaggio. Chiunque è libero di utilizzare e riprodurre questi sorgenti come meglio crede, purché nelle eventuali pubblicazioni sia citato il mio nome.
main
).
Una lista di interi è formata da elementi a valore intero. Il peso di
una lista di interi è dato dalla somma dei valori interi contenuti
nella lista stessa. Una lista multipla è formata da al più
T
liste di interi. Le operazioni che possono essere
effettuate su una lista multipla sono le seguenti:
ListaMultipla m()
m
. Inizialmente, tale lista multipla è vuota.
ListaMultipla m(n)
n
liste di interi. Inizialmente, tali liste di interi sono
vuote.
ListaMultipla m1(m)
m1
col valore della lista multipla m
.
m1 = m
m1
con quello della lista multipla
m
.
m.inserisci(i)
i
nella lista di interi a minor peso della lista multipla m
.
m.estrai()
m
, e ritorna il valore
di tale elemento.
m += n
n
liste di
interi alla lista multipla m
. Tali liste di interi sono
vuote.
m -= n
m
le n
liste di interi aventi peso
maggiore.
~ListaMultipla()
cout << m
[1, 2, 1] [2, 4, 3] [15] [2, 19]
. Le parentesi
quadre indicano una lista di interi. Le liste di interi vengono scritte
secondo pesi crescenti. In questo esempio, la lista multipla è formata
da quattro liste di interi; la terza di tali liste di interi è formata
da un solo elemento avente valore 15.
Utilizzando il linguaggio C++, realizzare il tipo di dati astratti
ListaMultipla
definito dalle precedenti
specifiche. Individuare eventuali situazioni di errore, e metterne in
opera un corretto trattamento.
Un Elemento
ha un peso di tipo intero e uno stato
trasparente od opaco. Le operazioni che possono essere effettuate su un
elemento sono le seguenti:
Elemento e()
e
. Inizialmente, tale elemento è trasparente ed ha peso 0.
Elemento e(s, n)
e
. Inizialmente,
tale elemento ha stato s
e peso n
.
Elemento e1(e)
e1
col
valore dell'elemento e
.
e1 = e
e1
con quello dell'elemento e
.
recente()
e.peso()
e
.
e.stato()
e
.
cout << e
Elemento
. L'uscita
specifica lo stato, 'T'
per trasparente ed
'O'
per opaco, ed il peso dell'elemento, separati dal
carattere ':'
. Esempio: T:100
.
cin >> e
Elemento
. L'operatore
legge il risultato dell'operatore di uscita per il tipo
Elemento
.
~Elemento()
Uno StackDiElementi
è in grado di contenere elementi in
numero limitato (la capacità dello stack di elementi). Le operazioni
che possono essere effettuate su uno stack di elementi sono le
seguenti:
StackDiElementi t()
t
avente capacità pari al valore dell'intero costante
M
. Inizialmente, tale stack di elementi è vuoto.
StackDiElementi t(n)
t
avente
capacità pari a n
elementi. Inizialmente, tale stack di
elementi è vuoto.
StackDiElementi t1(t)
t1
col valore dello stack di elementi t
.
t1 = t
t1
con quello dello stack di elementi
t
.
t.push()
t
un
elemento avente valore pari al valore di creazione dell'elemento creato
più di recente. L'operazione opera in accordo con la strategia LIFO.
t.push(e)
t
un
elemento avente valore pari al valore dell'elemento
e
. L'operazione opera in accordo con la strategia LIFO.
t.pop(s)
t
il primo
elemento di stato s
e ritorna il valore di tale
elemento. L'operazione opera in accordo con la strategia LIFO.
t.top(s)
s
dello stack di elementi t
. L'operazione
opera in accordo con la strategia LIFO.
t.vuoto()
t.pieno()
~StackDiElementi()
Mediante il linguaggio C++, realizzare il tipo di dati astratti
Elemento
, definito dalle precedenti specifiche.
Utilizzare il tipo di dati astratti Elemento per realizzare il tipo di
dati astratti StackDiElementi
facendo ricorso ad una
rappresentazione dello stack di elementi ad array. Individuare le
eventuali situazioni di errore, e metterne in opera un corretto
trattamento.
Un Dado
ha valore intero compreso tra 1 e 6. Le operazioni
che possono essere effettuate su un dado sono le seguenti:
Dado d()
d
. Inizialmente, tale dado ha valore 1.
Dado d1(d)
d1
col
valore del dado d
.
Dado d(n)
int
ovunque occorre un dado. Il costruttore inizializza il dado
d
col valore intero n
.
operator int(d)
int
. L'operatore ritorna il valore del dado
d
.
d1 = d
d1
con quello del dado d
.
~Dado()
Un LancioDiDadi
è in grado di contenere un numero limitato
di dadi. Le operazioni che possono essere effettuate su un lancio di
dadi sono le seguenti:
LancioDiDadi c()
c
. Tale lancio di dati può contenere al più un numero di
dadi pari al valore dell'intero costante M
. Inizialmente,
il lancio di dati non contiene dadi.
LancioDiDadi c1(c)
c1
col valore del lancio di dadi c
.
LancioDiDadi c(n)
int
ovunque occorre un lancio di dadi. Il costruttore inizializza un
lancio di dadi c
. Tale lancio di dati può contenere al più
n
dadi. Inizialmente, il lancio di dati non contiene dadi.
operator int(c)
int
. L'operatore ritorna la somma dei
valori dei dadi che formano il lancio di dadi c
.
c1 = c
c1
con quello del lancio di dadi c
.
c += d
d
al lancio di dadi c
.
c -= d
d
dal lancio di dadi c
.
c %= d
c
tutti i dadi aventi valore pari al dado d
.
!c
c
.
cout << c
LancioDiDadi
. L'uscita
consiste nei valori dei dadi che formano il lancio c
, in
ordine crescente, separati da virgole e racchiusi tra parentesi
graffe. Esempio: {1, 1, 1, 4, 5}
. In questo esempio, il
lancio è formato da cinque dadi, ed i primi tre hanno valore 1.
~LancioDiDadi()
Mediante il linguaggio C++, realizzare il tipo di dati astratti
Dado
, definito dalle precedenti specifiche. Utilizzare il
tipo Dado
per realizzare il tipo di dati astratti
LancioDiDadi
. Individuare le eventuali situazioni di
errore, e metterne in opera un corretto trattamento.
Un InsiemeDiInteri è formato interi diversi compresi tra 0 ed N-1, con N minore o uguale al numero di bit che formano un long int. Le operazioni che possono essere effettuate su un insieme di interi sono le seguenti:
InsiemeDiInteri d()
d
. Inizialmente, tale insieme di interi è vuoto.
InsiemeDiInteri d1(d)
d1
col valore dell'insieme di interi d
.
operator int(d)
int
. L'operatore ritorna la somma degli
interi che formano l'insieme di interi d
.
d1 = d
d1
con quello dell'insieme di interi
d
.
d += i
i
all'insieme di interi d
.
d -= i
i
dall'insieme di interi d
.
cout << d
d
, in
ordine crescente, separati da virgole e racchiusi tra parentesi
graffe. Esempio: {1, 3, 8, 14}
.
~InsiemeDiInteri()
Un Collezione è formata da insiemi di interi in numero limitato (la capienza della collezione). Le operazioni che possono essere effettuate su una collezione sono le seguenti:
Collezione c()
c
avente capienza pari al valore dell'intero costante M
.
Inizialmente, tale collezione è vuota.
Collezione c(m)
c
avente capienza m
. Inizialmente, tale collezione è vuota.
Collezione c1(c)
c1
col valore della collezione c
.
operator int(c)
int
. L'operatore ritorna la somma degli interi
che formano gli insieme di interi contenuti in c
.
c1 = c
c1
con quello della collezione c
.
c += d
d
alla collezione c
.
c -= d
d
dalla collezione c
.
cout << c
Collezione
. L'uscita
consiste nei valori degli insiemi di interi che formano la collezione
c
. Esempio: {1, 3, 8, 14} {1} {0, 3, 5}
. In
questo esempio, il secondo insieme della collezione è formato da un
solo intero avente valore 1
.
~Collezione()
Mediante il linguaggio C++, realizzare il tipo di dati astratti
InsiemeDiInteri
, definito dalle precedenti specifiche. Si
ipotizzi che Utilizzare il tipo InsiemeDiInteri
per
realizzare il tipo di dati astratti
Collezione
. Individuare le eventuali situazioni di errore,
e metterne in opera un corretto trattamento.
Una Striscia è formata da caselle trasparenti o colorate, ed i
possibili colori sono BIANCO
e NERO
. Le
caselle sono numerate a partire da 1. Le caselle bianche occupano
sempre le posizioni della striscia ai più bassi numeri d'ordine, le
caselle nere occupano sempre le posizioni della striscia ai più alti
numeri d'ordine. Le operazioni che possono essere effettuate su una
striscia sono le seguenti:
Striscia s()
s
formata da una sola casella. Inizialmente, tale casella è trasparente.
Striscia s(n)
s
formata da
n
caselle. Inizialmente, tali caselle sono trasparenti.
Striscia s1(s)
s1
col
valore della striscia s
.
s1 = s
s1
con quello della striscia s
.
s += c
s
. Tale casella assume il colore
c
.
s + c
c
ad una casella trasparente della striscia
s
.
s -= c
c
della striscia s
.
s - c
c
della striscia
s
.
s *= n
s
aggiungendo ad essa n
caselle trasparenti.
s /= n
s
eliminando da essa n
caselle trasparenti.
s % c
~Striscia()
cout << s
Striscia
. L'uscita ha la
forma seguente: [BBB^^^NNNNN]
. I caratteri
'B'
ed 'N'
rappresentano caselle di colore
BIANCO
e NERO
, rispettivamente, il carattere
'^'
rappresenta una casella trasparente.
cin >> s
Striscia
. L'operatore
legge il risultato dell'operatore di uscita per il tipo
Striscia
.
Utilizzando il linguaggio C++, realizzare il tipo di dati astratti
Striscia
, definito dalle precedenti specifiche. Si
individuino le eventuali situazioni di errore, e se ne metta in opera
un corretto trattamento.
Un Muro
è formato da colonne affiancate di mattoni
colorati, ed i possibili colori sono BIANCO
,
ROSSO
, GIALLO
e VERDE
. Le
colonne possono avere altezza diversa. Le operazioni che possono essere
effettuate su un muro sono le seguenti:
Muro m()
m
formato
da una sola colonna. Inizialmente, tale colonna non contiene mattoni.
Muro m(n)
m
formato da
n
colonne. Inizialmente, tali colonne non contengono
mattoni.
Muro m1(m)
m1
col
valore del muro m
.
m1 = m
m
.
m.aggiungi(c)
m
.
m.togli()
m
.
~Muro()
cout << m
[V R R B][B V V][R G G G]
. I caratteri `B'
,
`R'
, `G'
e `V'
rappresentano
mattoni di colore BIANCO
, ROSSO
,
GIALLO
e VERDE
, rispettivamente. Ciascuna
coppia di parentesi quadre specifica una colonna di mattoni, a partire
dal mattone più in basso nella colonna stessa. In questo esempio, il
muro è formato da tre colonne, la seconda delle quali contiene un
mattone di colore BIANCO
e, sopra di esso, due di colore
VERDE
.
Utilizzando il linguaggio C++, realizzare il tipo di dati astratti
Muro
, definito dalle precedenti specifiche. Si faccia
ricorso ad una rappresentazione a lista di ciascuna colonna. Si
individuino le eventuali situazioni di errore, e se ne metta in opera
un corretto trattamento.