Sei sulla pagina 1di 30

FONDAMENTI DI PROGRAMMAZIONE PROVA PRATICA 10 GENNAIO 2018

CORSO DI LAUREA IN INGEGNERIA INFORMATICA

Un GestoreApp rappresenta la lista di app in esecuzione in un certo momento su un dispositivo mobile,


per esempio uno smartphone o un tablet. Ogni app è identificata da un nome di massimo 20 caratteri. Non
esistono due app con lo stesso nome e i nomi delle app sono case-sensitive (cioè, “WhatsApp” è diverso da
“WHATSAPP”). Una app entra nella lista quando viene avviata, e ne esce quando viene chiusa. La prima
app della lista è quella in foreground, ovvero è quella visualizzata sullo schermo del dispositivo mobile.
Tutte le altre sono in background. Implementare le seguenti operazioni che possono essere effettuate su un
GestoreApp:

--- PRIMA PARTE --- (qualora siano presenti errori di compilazione, collegamento o esecuzione in questa
parte, l’intera prova sarà considerata insufficiente e pertanto non sarà corretta)
 GestoreApp g;
Costruttore di default che inizializza un GestoreApp vuoto, cioè senza nessuna app in esecuzione.
 g += str;
Operatore di somma e assegnamento che avvia una nuova app di nome str e la posiziona in foreground
(cioè, come prima della lista). L’app che era precedentemente in foreground diventa seconda della lista,
quella che era seconda diventa terza, e così via. Se una app con lo stesso nome era già in esecuzione, la lista
resta inalterata.
 cout << g;
Operatore di uscita per il tipo GestoreApp. L’operatore stampa tra parentesi quadre la app in foreground,
seguita dalle eventuali app in background separate da virgola e spazio, secondo questo formato:
[WhatsApp], Shazam, TripAdvisor, PlayStore
Se nessuna app è in esecuzione, stampa:
[]

--- SECONDA PARTE ---


 g.foreground(str);
Funzione che porta in foreground la app di nome str. La lista resta inalterata se una app con quel nome è
già in foreground oppure non è in esecuzione.
 g -= str;
Operatore di sottrazione e assegnamento che chiude la app di nome str. Se la app da chiudere è in
foreground, viene mandata in foreground la seconda della lista, se esiste. Se una app con quel nome non è in
esecuzione, la lista resta inalterata.
 g.chiudi_tutte();
Funzione che chiude tutte le app in esecuzione.
 ~GestoreApp();
Distruttore.

Mediante il linguaggio C++, realizzare il tipo di dato astratto GestoreApp, definito dalle precedenti
specifiche. Gestire le eventuali situazioni di errore.
USCITA CHE DEVE PRODURRE IL PROGRAMMA
--- PRIMA PARTE ---
Test del costruttore:
[]

Test operatore +=:


[WhatsApp]
[Shazam], TripAdvisor, PlayStore, WhatsApp

--- SECONDA PARTE ---

Test di foreground:
[TripAdvisor], Shazam, PlayStore, WhatsApp
[TripAdvisor], Shazam, PlayStore, WhatsApp

Test operatore -=:


[TripAdvisor], PlayStore, WhatsApp
[TripAdvisor], PlayStore, WhatsApp

Test di chiudi_tutte:
[]

Test distruttore:
[Facebook], YouTube
(g e' stato distrutto)
.h
#include <iostream>

using namespace std;

struct elem{
char nome[21];
elem* pun;
};

class GestoreApp{
elem* testa;
public:
GestoreApp();
GestoreApp& operator+=(const char*);
friend ostream& operator<<(ostream&, const GestoreApp&);

// --- SECONDA PARTE ---


bool foreground(const char*);
GestoreApp& operator-=(const char*);
void chiudi_tutte();
~GestoreApp();
};

cpp

#include "compito.h"
#include <cstring>

GestoreApp::GestoreApp(){
testa = NULL;
}

GestoreApp& GestoreApp::operator+=(const char* nome){


elem* curr;
for(curr = testa; curr != NULL; curr = curr->pun){
if(strcmp(curr->nome, nome) == 0)
return *this;
}
elem* app = new elem;
strcpy(app->nome, nome);
app->pun = testa;
testa = app;
return *this;
}

GestoreApp& GestoreApp::operator-=(const char* nome){


elem* curr;
elem* prev = NULL;
for(curr = testa; curr != NULL; curr = curr->pun){
if(strcmp(curr->nome, nome) == 0)
break;
prev = curr;
}
if(curr == NULL)
return *this;

if(curr == testa)
testa = testa->pun;
else
prev->pun = curr->pun;

delete curr;
return *this;
}

bool GestoreApp::foreground(const char* nome){


elem* curr;
elem* prev = NULL;
for(curr = testa; curr != NULL; curr = curr->pun){
if(strcmp(curr->nome, nome) == 0)
break;
prev = curr;
}
if(curr == NULL)
return false;

if(curr == testa)
return true;

prev->pun = curr->pun;
curr->pun = testa;
testa = curr;
return true;
}

ostream& operator<<(ostream& os, const GestoreApp& g){


if(g.testa == NULL){
os << "[]";
return os;
}

os << '[' << g.testa->nome << ']';

const elem* curr;


for(curr = g.testa->pun; curr != NULL; curr = curr->pun){
os << ", " << curr->nome;
}
return os;
}

void GestoreApp::chiudi_tutte(){
elem* curr = testa;
elem* next;
while(curr != NULL){
next = curr->pun;
delete curr;
curr = next;
}
testa = NULL;
}

GestoreApp::~GestoreApp(){
chiudi_tutte();
}
FONDAMENTI DI PROGRAMMAZIONE PROVA PRATICA 16 FEBBRAIO 2018
CORSO DI LAUREA IN INGEGNERIA INFORMATICA

Il tipo di dato astratto ProntoSoccorso memorizza i pazienti in attesa di un pronto soccorso. I pazienti
sono identificati da una stringa contenente il proprio nome e cognome. La stringa ha almeno un carattere e
ne può contenere al massimo 20. A ogni paziente in arrivo viene associato un livello di priorità codificato
con quattro colori, in ordine di priorità crescente: bianco, verde, giallo e rosso.
I pazienti vengono trattati in ordine di priorità. A parità di livello di priorità, i pazienti vengono trattati
secondo l’ordine di arrivo. Dopo che un paziente è stato trattato, lascia il pronto soccorso.
Implementare le seguenti operazioni che possono essere effettuate su un ProntoSoccorso:
--- Metodi invocati nella PRIMA PARTE di main.cpp: ---
 ProntoSoccorso ps;
Costruttore che inizializza un ProntoSoccorso. Inizialmente, non ci sono pazienti in attesa.
 ps.ricovero(nome, liv);
Ricovera il paziente di nome nome, assegnandogli il livello di priorità liv. La funzione non modifica lo
stato di ps se nome assume un valore non valido.
 ps.prossimo(nome);
Seleziona il prossimo paziente per il trattamento secondo l’ordine di priorità. Il nome del paziente
selezionato viene restituito nella stringa nome. Il valore di ritorno della funzione è 0 se non ci sono pazienti
in attesa, altrimenti è 1.
 cout << ps;
Operatore di uscita per il tipo ProntoSoccorso. L’uscita ha il seguente formato:
Numero pazienti: 4
[CODICE ROSSO]
->Mario Rossi
->Giuseppe Gialli
[CODICE GIALLO]
[CODICE VERDE]
->Paolo Verdi
[CODICE BIANCO]
->Maria Neri
L’output mostrato corrisponde a un ProntoSoccorso avente quattro pazienti in attesa: due sono in
codice rosso, uno in codice verde e uno in codice bianco.
--- Metodi invocati nella SECONDA PARTE di main.cpp: ---
 ProntoSoccorso ps1(ps);
Costruttore di copia per il tipo ProntoSoccorso, che crea un pronto soccorso ps1 uguale a ps.
 ps1 = ps;
Operatore di assegnamento per il tipo ProntoSoccorso, che rende ps1 uguale a ps.
 ~ProntoSoccorso();
Distruttore.

Mediante il linguaggio C++, realizzare il tipo di dato astratto ProntoSoccorso, definito dalle precedenti
specifiche. Gestire le eventuali situazioni di errore.

continua sul retro del foglio


OUTPUT ATTESO DAL PROGRAMMA

---PRIMA PARTE---
Test del costruttore di default:
Numero
pazienti: 0
[CODICE ROSSO]
[CODICE GIALLO]
[CODICE VERDE]
[CODICE BIANCO]

Test di
ricovero:
Numero
pazienti: 4
[CODICE ROSSO]
->Mario Rossi
->Giuseppe
Gialli [CODICE
GIALLO]
[CODICE VERDE]
->Paolo
Verdi
[CODICE
BIANCO]
->Maria Neri

Test di prossimo:
Prossimo: Mario Rossi
Prossimo: Giuseppe
Gialli Prossimo: Paolo
Verdi

Numero
pazienti: 1
[CODICE ROSSO]
[CODICE GIALLO]
[CODICE VERDE]
[CODICE BIANCO]
->Maria Neri

---SECONDA PARTE---
Test dell'operatore
=: Numero pazienti:
1 [CODICE ROSSO]
[CODICE GIALLO]
[CODICE VERDE]
[CODICE BIANCO]
->Maria Neri

Test del costruttore di copia:


Numero
pazienti: 3
[CODICE ROSSO]
[CODICE GIALLO]
[CODICE VERDE]
->Luigi
Viola
[CODICE
BIANCO]
->Maria Neri
->Carlo Bianchi

Test del distruttore:


(ps2 e' stato
distrutto)
#ifndef COMPITO_H_INCLUDED
#define COMPITO_H_INCLUDED

#include <iostream>
using namespace std;

const int MAXCHAR = 20;


enum Priorita {ROSSO, GIALLO, VERDE, BIANCO};

struct elem {
char nome[MAXCHAR+1];
elem* pun;
};
typedef elem* Lista;

class ProntoSoccorso
{
Lista p[4]; // una lista di pazienti per ogni livello
int numPazienti; // numero totale di pazienti

// funzioni di utilita'
void inserisci(Lista&, const char*);
int estrai(Lista&, char*);
void copia(Lista, Lista&);
void distruggi(Lista&);
public:
ProntoSoccorso();
void ricovero(const char*, Priorita);
int prossimo(char*);
friend ostream& operator<< (ostream&, const ProntoSoccorso&);
ProntoSoccorso(const ProntoSoccorso&);
ProntoSoccorso& operator=(const ProntoSoccorso&);
~ProntoSoccorso();
};

#endif // COMPITO_H_INCLUDED

#include "compito.h"
#include <cstring>

/*
* Funzioni di utilità
*/

void ProntoSoccorso::inserisci(Lista& testa, const char* nome)


{
if (testa == NULL)
{
testa = new elem;
strcpy(testa->nome,nome);
testa->pun = NULL;
}
else
inserisci(testa->pun, nome);
}

int ProntoSoccorso::estrai(Lista& testa, char* nome)


{
if ( testa != NULL )
{
Lista p = testa;
testa = testa->pun;
strcpy(nome,p->nome);
delete p;

return 1;
}
else
return 0;
}

void ProntoSoccorso::copia(Lista src, Lista& dst)


{
if (src == NULL)
dst = NULL;
else
{
// si copia testa della lista
dst = new elem;
strcpy(dst->nome, src->nome);
dst->pun = NULL;

// si copia il resto della lista


Lista p = src->pun;
Lista q = dst;
while (p != NULL)
{
q->pun = new elem;
q = q->pun;
strcpy(q->nome, p->nome);
q->pun = NULL;
p = p->pun;
}
}
}

void ProntoSoccorso::distruggi(Lista& testa)


{
if (testa != NULL)
{
distruggi( testa->pun );
delete testa;
testa = NULL;
}
}

/*
* Operazioni su Pronto Soccorso
*/

ProntoSoccorso::ProntoSoccorso()
{
p[0] = p[1] = p[2] = p[3] = NULL;
numPazienti = 0;
}

void ProntoSoccorso::ricovero(const char* nome, Priorita liv)


{
if (nome == NULL)
return;
if (strlen(nome) < 1 || strlen(nome) > MAXCHAR)
return;

// si inserisce il paziente nella lista corrispondente al livello di priorita'


inserisci(p[liv], nome);
numPazienti++;
}

int ProntoSoccorso::prossimo(char* nome)


{
if (nome == NULL)
return 0;

// si scorrono le liste in ordine di priorita'


for (int i = 0; i < 4; i++)
{
if (p[i] != NULL)
{
numPazienti--;
return estrai(p[i], nome);
}
}
return 0;
}

ostream& operator<<(ostream& os, const ProntoSoccorso& ps)


{
os << "Numero pazienti: " << ps.numPazienti << endl;

Lista testa;
for (int i = 0; i < 4; i++)
{
os << "[CODICE ";
switch(i)
{
case 0: os << "ROSSO"; break;
case 1: os << "GIALLO"; break;
case 2: os << "VERDE"; break;
case 3: os << "BIANCO";
}
os << "]" << endl;

// scorro la lista corrispondente a questo livello


testa = ps.p[i];
while ( testa != NULL )
{
os << "->" << testa->nome << endl;
testa = testa->pun;
}
}
return os;
}

ProntoSoccorso::ProntoSoccorso(const ProntoSoccorso& src)


{
numPazienti = src.numPazienti;
for (int i = 0; i < 4; i++ )
copia(src.p[i], p[i]);
}

ProntoSoccorso& ProntoSoccorso::operator=(const ProntoSoccorso& src)


{
if (this != &src)
{
numPazienti = src.numPazienti;
for (int i = 0; i < 4; i++)
{
distruggi(p[i]);
copia(src.p[i], p[i]);
}
}
return *this;
}

ProntoSoccorso::~ProntoSoccorso()
{
for (int i = 0; i < 4; i++)
distruggi(p[i]);
}
FONDAMENTI DI INFORMATICA I
PROVA PRATICA 20 LUGLIO
2016
CORSO DI LAUREA IN INGEGNERIA INFORMATICA

Un NastroTrasportatore è un dispositivo che trasporta pezzi meccanici. I pezzi vengono inseriti da


un’estremità del nastro e vengono rimossi dall’altra estremità. Ogni pezzo è caratterizzato da un
identificatore di lunghezza variabile. In particolare, l’identificatore è composto da una lettera maiuscola
seguita da una o più cifre. Mentre i pezzi circolano sul nastro, un operatore effettua il controllo della qualità
su pezzi scelti a caso dal nastro, marcandoli come controllati.
Implementare le seguenti operazioni che possono essere fatte su NastroTrasportatore:
--- PRIMA PARTE --- (qualora siano presenti errori di compilazione, collegamento o esecuzione in questa
parte, l’intera prova sarà considerata insufficiente e pertanto non sarà corretta)
 
NastroTrasportatore n;
Costruttore che inizializza un nastro trasportatore. Inizialmente, non ci sono pezzi sul nastro.
 
n.aggiungi(id);
Operazione che inserisce sul nastro trasportatore il pezzo avente identificatore id. L’operazione può fallire
se l’identificatore del pezzo non ha il formato corretto.
 
n.rimuovi();
Operazione che rimuove dal nastro trasportatore il pezzo che ha raggiunto la fine del nastro.
 
cout << s;
Operatore di uscita per il tipo NastroTrasportatore. L’uscita ha il seguente formato:
(C878)=(A1625)=(B42)
L’output mostrato a video corrisponde a un nastro su cui sono presenti tre pezzi. Il nastro si muove da
sinistra a destra, quindi B42 è il primo pezzo a essere stato inserito e C878 l’ultimo. Nel caso non ci siano
pezzi sul nastro, l’output mostrato è il seguente:
<vuoto>
 
NastroTrasportatore n1(n);
Costruttore di copia per il tipo NastroTrasportatore.

--- SECONDA PARTE ---


 
n.controlla(i);
Operazione che effettua il controllo della qualità sull’i-esimo pezzo che si trova sul nastro. Il primo pezzo
sul nastro (partendo da sinistra) ha posizione i=1. In particolare, il pezzo viene tolto momentaneamente dal
nastro, viene marcato come controllato e poi ricompare sul nastro nella posizione immediatamente
precedente.
Esempio: dato il seguente nastro trasportatore:
(C878)=(A1625)=(B42)
Invocando la funzione con i=2, il nastro si modifica nel seguente modo:
(A1625*)=(C878)=(B42)
dove il pezzo avente identificatore A1625 è stato marcato come controllato (nell’output deve comparire il
carattere ‘*’) e reinserito dopo il pezzo che lo seguiva sul nastro, ovvero quello avente identificatore C878.
Se il pezzo è già marcato come controllato, il controllo non deve essere rieseguito e il nastro rimane
inalterato.
 
int(n);
Operatore di conversione, che consente di utilizzare un NastroTrasportatore ovunque serva un
intero. L’operatore ritorna il numero di pezzi presenti sul nastro sui quali è stato effettuato il controllo.
 
 ~NastroTrasportatore();
Distruttore.

Mediante il Linguaggio C++, realizzare il tipo di dato astratto NastroTrasportatore, definito dalle
precedenti specifiche. Gestire le eventuali situazioni di errore.

USCITA CHE DEVE PRODURRE IL PROGRAMMA


--- PRIMA PARTE ---
Test del costruttore
<vuoto>

Test della aggiungi()


(F024)
(B42)=(F024)
(A1625)=(B42)=(F024)
(C878)=(A1625)=(B42)=(F024)

Test della rimuovi()


(C878)=(A1625)=(B42)

Test del costruttore di copia


(C878)=(A1625)=(B42)

--- SECONDA PARTE ---


Test della controlla()
(A1625*)=(C878)=(B42)
(A1625*)=(B42*)=(C878)

Test di int()
2

Test del distruttore


(n2 e' stato distrutto)
#include <iostream>
using namespace std;

struct pezzo{
char* id;
bool controllato;
pezzo* pun;
};

class NastroTrasportatore{
pezzo* _testa;

public:
// --- PRIMA PARTE ---
NastroTrasportatore();
void aggiungi(const char*);
void rimuovi();
friend ostream& operator<<(ostream&, const NastroTrasportatore&);
NastroTrasportatore(const NastroTrasportatore&);

// --- SECONDA PARTE ---


operator int() const;
void controlla(unsigned int);
~NastroTrasportatore();
};

cpp

#include "compito.h"
#include <cstring>

NastroTrasportatore::NastroTrasportatore() {
_testa = NULL;
}

NastroTrasportatore::NastroTrasportatore(const NastroTrasportatore &n) {


_testa = NULL;

pezzo *ultimo;
for (pezzo *p = n._testa; p != NULL; p = p->pun) {
pezzo *q = new pezzo;
q->id = new char[strlen(p->id) + 1];
strcpy(q->id, p->id);
q->controllato = p->controllato;
q->pun = NULL;

// inserimento
if (_testa == NULL)
_testa = q;
else
ultimo->pun = q;

ultimo = q;
}
}

void NastroTrasportatore::aggiungi(const char *id) {


// la stringa ha almeno due caratteri (lettera+cifra)
if (strlen(id) < 2)
return;

// controllo prima lettera


if (id[0] < 'A' || id[0] > 'Z')
return;
// controllo se i restanti caratteri sono cifre
for (int i = 1; i < strlen(id); i++) {
if (id[i] < '0' || id[i] > '9')
return;
}

// inserimento in _testa
pezzo *p = new pezzo;
p->id = new char[strlen(id) + 1];
strcpy(p->id, id);
p->controllato = false;
p->pun = _testa;
_testa = p;
}

void NastroTrasportatore::rimuovi() {
if (_testa == NULL)
return;

pezzo *p, *q;


for (p = _testa; p->pun != NULL; p = p->pun)
q = p;

// controllo se la lista contiene un solo elemento


if (p == _testa)
_testa = NULL;
else
q->pun = NULL;

delete[] p->id;
delete p;
}

ostream &operator<<(ostream &os, const NastroTrasportatore &n) {


if (n._testa == NULL)
os << "<vuoto>";
else {
for (pezzo *p = n._testa; p != NULL; p = p->pun) {
if (p != n._testa)
os << "=";

os << "(" << p->id;


if (p->controllato)
os << "*";

os << ")";
}
}
return os;
}

// --- SECONDA PARTE ---

void NastroTrasportatore::controlla(unsigned int i) {


// controllo validita' indice e caso lista vuota
if (i < 1 || _testa == NULL)
return;

// caso particolare: controllo del primo elemento


if (i == 1) {
_testa->controllato = true;
return;
}

unsigned int c = 1;
pezzo *p = _testa;
pezzo *q = p;
pezzo *r;
while (p != NULL && c < i) {
r = q; // r rimane due passi indietro a p
q = p; // q rimane un passo indietro a p
p = p->pun;
c++;
}

// ho raggiunto la fine della lista


// (non esiste l'i-esimo pezzo)
if (p == NULL)
return;

// controlla se pezzo gia' controllato


if (p->controllato)
return;

p->controllato = true;

// caso particolare: sono sul secondo elemento e lo


// devo spostare in testa
if (r == q) {
_testa->pun = p->pun;
p->pun = _testa;
_testa = p;
} else {
q->pun = p->pun;
r->pun = p;
p->pun = q;
}
}

NastroTrasportatore::operator int() const {


unsigned int ret = 0;
for (pezzo *p = _testa; p != NULL; p = p->pun) {
if (p->controllato)
ret++;
}
return ret;
}

NastroTrasportatore::~NastroTrasportatore() {
while (_testa != NULL) {
pezzo *p = _testa;
_testa = _testa->pun;
delete[] p->id;
delete p;
}
}
FONDAMENTI DI INFORMATICA I PROVA PRATICA 17 GENNAIO 2015
FOND. DI INFORMATICA E PROGRAMMAZIONE A OGGETTI
CORSO DI LAUREA IN INGEGNERIA INFORMATICA

Un Ponte Mobile è un particolare tipo di ponte che,


quando è chiuso, consente alle automobili di
attraversare il fiume come un qualunque altro normale
ponte. A differenza dei comuni ponti esso è apribile:
quando è aperto, esso consente il transito alle navi (in
questo stato il transito delle automobili è precluso). In
particolare, quando il Ponte Mobile è aperto le
automobili si pongono in attesa in base all’ordine di
arrivo. Quando il ponte si chiude, tutte le auto in attesa
vengono fatte passare. Quando il ponte è chiuso non ci
sono auto in attesa. Ogni auto è identificata in modo
univoco da una stringa di al
più 6 lettere maiuscole dell’alfabeto.
Implementare le seguenti operazioni che possono essere compiute su un PonteMobile:
PRIMA PARTE (qualora siano presenti errori di compilazione, collegamento o esecuzione in
questa parte, l’intera prova verrà considerata insufficiente e pertanto non verrà corretta)

 PonteMobile p(s);
Costruttore che crea un ponte mobile. Il ponte è aperto ed è presente un’automobile con identificatore
s in attesa di fronte a p.
 p.aggiungi(s);
Operazione che aggiunge l’auto con identificatore s a quelle in attesa al ponte p. Se il ponte è chiuso,
l’auto viene fatta passare. Se il ponte è aperto l’auto viene messa in attesa. NB: se un’auto con lo
stesso identificatore esiste già in attesa, non viene effettuata l’aggiunta della nuova auto. In
quest’ultimo caso il ponte rimane inalterato (non sono ammessi identificatori duplicati).
 cout<<p;
Operatore di uscita del ponte mobile. Innanzitutto viene mostrato lo stato del ponte:
PONTE APERTO o PONTE CHIUSO.
Nel caso di ponte aperto, vengono anche mostrati gli identificatori delle auto in attesa in ordine di
arrivo (premettere i caratteri “=>” ad ogni identificatore). Nell’esempio seguente il ponte p è aperto
e ci sono 3 auto in attesa. L’identificatore dell’auto arrivata per prima è AAA, quello dell’auto
arrivata per ultima è C.

PONTE APERTO=>AAA=>BB=>C
SECONDA PARTE (si invita a mettere sotto commento le operazioni di questa seconda parte che
dovessero impedire la compilazione, il collegamento o la corretta esecuzione del codice)

 p.cambiaStato();
Operazione che modifica lo stato del ponte. Se il ponte cambia stato da aperto a chiuso, l’operazione
fa passare tutte le auto in attesa.
 p -= s;
Operatore di sottrazione e assegnamento che modifica il ponte mobile p eliminando l’auto con
identificatore s da quelle in attesa. Se l’auto non è presente, il ponte mobile rimane inalterato.
 ~p;
Operatore di complemento bit a bit che restituisce il numero di auto in attesa di fronte al ponte p.
 ~PonteMobile();
Distruttore

Mediante il linguaggio C++, realizzare il tipo di dato astratto PonteMobile, definito dalle precedenti
specifiche. Individuare eventuali situazioni di errore, e metterne in opera un corretto trattamento.

NOTE SULLO SVOLGIMENTO DELLA PROVA PRATICA

AVVIO E IDENTIFICAZIONE
• Avviare la macchina in modalità diskless, scegliere
“Fondamenti di Informatica I” ed effettuare il login:
nome: studenti password: studenti
• Aprire un terminale e al prompt spostarsi sulla cartella ‘elaborato’ ($
cd ~/elaborato). Si utilizzi il comando pwd per verificare che ci si trovi
nella cartella corretta /home/studenti/elaborato.
• Sempre al prompt dare il comando ident, sempre da dentro la cartella. Lo
script richiede i propri dati (cognome, nome, numero di matricola e
password (la password non va dimenticata in quanto è indispensabile per
scaricare da internet il proprio elaborato a consegna avvenuta). Il
comando ident crea il file matricola.txt nella cartella corrente. Lo
script può essere lanciato più volte, in tal caso il file matricola.txt
viene sovrascritto. Per verificare che il file sia stato creato e che il
contenuto sia quello giusto dare il comando (la password è codificata):
$ cat /home/studenti/elaborato/matricola.txt
• A questo punto il docente verifica che tutti gli studenti abbiamo
effettuato l’identificazione, dopodiché provvede a inviare i seguenti file
nella cartella elaborato del proprio PC: compito.h, compito.cpp, main.cpp.
Controllare pertanto che questi file, insieme al file matricola.txt, siano presenti sul proprio elaboratore.
SVOLGIMENTO DELLA PROVA
• Definire ed implementare il tipo di dato astratto richiesto e le
relative funzioni nei file compito.h e compito.cpp. Il file main.cpp
contiene la funzione principale main() ed è utilizzato dallo studente per
testare la sua implementazione della classe. Il file main.cpp può essere
modificato a piacere. In sede di valutazione dell’elaborato verrà
considerato esclusivamente il contenuto dei file compito.h e compito.cpp
ed è pertanto vietato cambiare nome a tali file.
Per compilare e linkare dare il comando:
$ g++ main.cpp compito.cpp (eseguibile invocabile tramite $ ./a.out)
(utilizzare g++ –g per includere le informazioni di debug qualora si intenda debuggare con ddd).
PER CONSEGNARE O RITIRARSI
Recarsi dal docente dopo aver preso nota dell’identificativo della macchina (esempi: g34, s23, c22, …).

USCITA CHE DEVE PRODURRE IL PROGRAMMA

Test costruttore e op. di uscita (deve stampare 'PONTE


APERTO=>AAA') PONTE APERTO=>AAA

Test della aggiungi (deve stampare 'PONTE


APERTO=>AAA=>BB=>C') PONTE APERTO=>AAA=>BB=>C

Altro test della aggiungi e dell'op. di compl. bit a bit (deve


stampare 3) 3
Test operatore -= (deve stampare 'PONTE
APERTO=>AAA=>C') PONTE APERTO=>AAA=>C

Test della cambiaStato (deve stampare 'PONTE


CHIUSO') PONTE CHIUSO

#include <iostream>
#include <cstring>

using namespace std;

enum stato{APERTO,CHIUSO};

struct elem {
char identificatore[7];
elem* pun;
};

class PonteMobile{
// membri dato
stato st;
elem* testa;

// funzioni di utilita'
void eliminaLista();
bool identificatoreValido(const char*);

// mascheramento costruttore di copia


PonteMobile(const PonteMobile&);

public:
// PRIMA PARTE
PonteMobile(const char*);
friend ostream& operator<<(ostream&, const PonteMobile&);
PonteMobile& aggiungi(const char*);

// SECONDA PARTE
void cambiaStato();
friend int operator~(const PonteMobile&); // implementazione come operatore globale
PonteMobile& operator-=(const char*);
~PonteMobile(){eliminaLista();}
};
#include "compito.h"

void PonteMobile::eliminaLista(){
elem* p = testa;
while (testa!=NULL) {
testa = testa->pun;
delete p;
p=testa;
}
}

bool PonteMobile::identificatoreValido(const char *s){


int lun = strlen(s);
if (lun > 6)
return false;
for(int i = 0; i < lun; i++)
if ((s[i]<'A')||(s[i]>'Z'))
return false;
return true;
}

PonteMobile::PonteMobile(const char* s){


st = APERTO;
if (identificatoreValido(s)){
testa = new elem;
testa->pun = NULL;
strcpy(testa->identificatore, s);
}else
testa=NULL;
}

void PonteMobile::cambiaStato(){
switch(st){
case APERTO:
st = CHIUSO;
eliminaLista();
break;
case CHIUSO:
st = APERTO;
}
}

PonteMobile& PonteMobile::aggiungi(const char* s){


if ((st == CHIUSO) || (!identificatoreValido(s)))
return *this;
elem *p, *q;
for(q = testa; q!= NULL; q=q->pun) {
if (!strcmp(q->identificatore, s))
return *this;
p=q;
}
elem* r= new elem;
strcpy(r->identificatore, s);
r->pun = NULL;
if (testa==NULL)
testa = r;
else
p->pun = r;

return *this;
}

PonteMobile& PonteMobile::operator-=(const char* s){


elem* q, *prec;
for(q=testa; q!=NULL && strcmp(q->identificatore, s); q=q->pun)
prec = q;
if (q == NULL)
return *this;
// eliminazione
if(q==testa)
testa = testa->pun;
else
prec->pun = q->pun;

delete q;
return *this;
}

// (esercizio per casa: provare ad implementare il prossimo operatore


// come funzione membro invece che come funzione globale)
int operator~(const PonteMobile &p){
elem* q;
int conta=0;
for(q = p.testa; q != NULL; q = q->pun)
conta++;
return conta;
}

ostream& operator<<(ostream& os, const PonteMobile& p){


switch(p.st) {
case APERTO:
os << "PONTE APERTO";
elem* q;
for(q = p.testa; q != NULL; q = q->pun)
os << "=>" << q->identificatore;
os<<endl;
break;
case CHIUSO: os << "PONTE CHIUSO" << endl;
}
return os;
}
Utilità
#include <iostream>
#include <cstring>
using namespace std;

class Concessionaria {
struct elem {
char *nome;
elem *pun;
};
elem* _testa;
int _quante;
// funzione di utilità
bool contiene(const char*, const char*)const;
// mascheramento costruttore di copia e op. di assegnamento
Concessionaria(const Concessionaria&);
Concessionaria& operator=(const Concessionaria&);
public:
Concessionaria(){ _testa = NULL; _quante = 0;};
Concessionaria& operator+=(const char*);
friend ostream& operator << (ostream&,const Concessionaria&);
operator int()const {return _quante;};
// SECONDA PARTE
Concessionaria& operator -=(int);
int cerca(const char*)const;
~Concessionaria(){ *this-=_quante; };
};

// SECONDA PARTE
bool Concessionaria::contiene(const char *str, const char *substr)const{
int len = strlen(substr);
for (int k = 0; k <= strlen(str)-len; k++)
if ( !strncmp(&str[k],substr,len) )
return true;
return false;
}

int Concessionaria::cerca(const char* substr)const{


if (strlen(substr)){
int aux = 0;
for (elem* p =_testa; p != NULL; p=p->pun)
if (contiene(p->nome, substr))
aux++;
return aux;
}else
return 0;
}

// --- SECONDA PARTE ---


// funzione di utilità
void Molecola::dealloca(){
while (testa != NULL){
elem* p = testa;
testa = testa->pun;
delete p;
}
}
Operazione che modifica m1, sostituendo al suo precedente contenuto quello di m
Molecola& Molecola::operator=(const Molecola& m1){
if (this != &m1){
dealloca();
if (m1.testa == NULL) testa = NULL;
else{
testa = new elem;
testa->at = m1.testa->at;
elem* p = testa;
elem* q = m1.testa->pun;
while (q != NULL){
p->pun = new elem;
p = p->pun;
p->at = q->at;
q = q->pun;
}
p->pun = NULL;
}
}
return *this;
}
Operatore di somma e assegnamento che, se necessario, aggiunge nuovi atomi a m in
modo tale che la sigla di ciascun atomo di m1 sia presente anche in m. Per gli
atomi presenti sia in m che in m1, la quantità è la somma delle quantità in m e m1.
Per gli atomi presenti solo in m, la quantità rimane invariata. Per gli atomi
presenti solo in m1, la quantità è quella in m1.
Molecola& Molecola::operator+=(const Molecola& m1){
elem* p = m1.testa;
while (p != NULL){
aggiungi(p->at.sigla, p->at.quanti);
p = p->pun;
}
return *this;
}
Array di liste
FONDAMENTI DI INFORMATICA I PROVA PRATICA 1 FEBBRAIO 2016
CORSO DI LAUREA IN INGEGNERIA INFORMATICA

Uno Schedario può contenere documenti. Ogni documento è provvisto di un intero che ne
definisce la tipologia. Nello Schedario i documenti sono organizzati su tre livelli di priorità,
numerati da uno (livello più basso) a tre (livello più alto). Pertanto quando si vuole inserire un
nuovo documento nello Schedario, oltre alla tipologia, occorre specificare anche il livello di
priorità con cui si vuole inserire nello schedario.
Implementare le seguenti operazioni che possono essere compiute su di uno Schedario:

--- PRIMA PARTE --- (qualora siano presenti errori di compilazione, collegamento o esecuzione
in questa parte, l’intera prova sarà considerata insufficiente e pertanto non sarà corretta)

 Schedario s;
Costruttore che crea uno Schedario vuoto (non vi sono documenti, per nessun livello di priorità).
Uno schedario vuoto viene mostrato a video nel seguente formato:
L1()L2()L3()

 s.aggiungi(liv,tip);
Operazione che aggiunge un documento di tipologia tip al livello di priorità liv nello schedario
s, dove liv è compreso tra 1 e 3 e tip è un intero qualsiasi. L’inserimento avviene in modo tale
che, qualora s venga stampato a video, il documento inserito per ultimo venga mostrato a video
per primo tra quelli del livello di priorità liv.

 cout << s;
Operatore di uscita per il tipo Schedario. L’uscita ha il seguente formato:
L1(6)L2(35,48,21)L3(10,12)
L’output mostrato corrisponde a uno Schedario con un documento di tipologia 6 al livello di
priorità più basso, tre documenti (di tipologia 35, 48 e 21) a livello 2 e due documenti (di tipologia 10
e 12) a livello di priorità massimo (l’ultimo documento inserito a livello 2 è di tipologia 35, l’ultimo a
livello 3 è di tipologia 10).

 s-=liv;
Operazione che modifica lo schedario s eliminando tutti i documenti aventi priorità liv.
Esempio: Nel caso in cui s sia il seguente:
L1(6)L2(35,48,21)L3(10,12)
dopo l’esecuzione di s-=2 l’uscita a video di s produce:
L1(6)L2()L3(10,12)
--- SECONDA PARTE ---

 s.promuovi(liv,tip);
Operazione di che sposta tutti i documenti di tipologia tip che si trovano al livello di priorità liv al livello
immediatamente più alto (ovviamente questa operazione avrà un qualche effetto su s se e solo se liv è uno
oppure due, altrimenti lo schedario viene lasciato inalterato).
Esempio: Nel caso in cui s si trovi in questo stato:
L1(6)L2(5,8,5,5,3,5)L3(10,12)
dopo l’operazione s.promuovi(2,5) l’uscita a video di s produce:
L1(6)L2(8,3)L3(5,5,5,5,10,12)
NB: nell’implementare questa funzione si possono utilizzare funzioni ausiliarie, ma si deve contestualmente
cercare di scorrere la struttura dati una sola volta.

 ~Schedario();
Distruttore.

Mediante il Linguaggio C++, realizzare il tipo di dato astratto Schedario, definito dalle precedenti
specifiche. Gestire le eventuali situazioni di errore.

USCITA CHE DEVE PRODURRE IL PROGRAMMA

--- PRIMA PARTE ---

Test del costruttore e dell'operatore di uscita


L1()L2()L3()

Test della aggiungi


L1(6)L2(35,48,21)L3(10,12)

Test dell'operatore -=
L1(6)L2()L3(10,12)

--- SECONDA PARTE ---

Test della promuovi


[s prima della promuovi]
L1(6)L2(5,8,5,5,3,5)L3(10,12)
[s dopo la promuovi] L1(6)L2(8,3)L3(5,5,5,5,10,12)

Test del distruttore (s e' stato appena distrutto)


#include <iostream>
#include <cstring>
using namespace std;

const int DIM = 3;


struct elem{
int info;
elem* pun;
};

class Schedario{

elem* p0[DIM];

// funzioni di utilita'
void svuota(elem*&);

// mascheramento costruttore di copia e op. di assegnamento


Schedario(const Schedario&);
Schedario& operator=(const Schedario&);

public:
// --- PRIMA PARTE --- // 20 pt
Schedario();
void aggiungi(int, int);
Schedario& operator-=(int);
friend ostream& operator<<(ostream&, const Schedario&);

// --- SECONDA PARTE --- // 10 pt


void promuovi(int,int); // 7pt
~Schedario(); // 3pt
};

cpp
#include "compito.h"

Aula::Aula(int N){
totalePostazioni = ( N <= 0) ? 1: N;
v = new char* [totalePostazioni];
for (int i = 0; i < totalePostazioni; i++)
v[i] = NULL;
quanteOccupate = 0;
}

ostream& operator<<(ostream& os, const Aula& p){


for(int i = 0; i< p.totalePostazioni; i++){
if ( p.v[i] != NULL )
os << "POSTAZIONE" << (i+1) << ":"<<p.v[i]<<endl;
else
os << "POSTAZIONE" << (i+1) << ":<libera>"<<endl;
}
return os;
}

bool Aula::aggiungi(const char id[]){

if (quanteOccupate == totalePostazioni)
return false;

for (int i = 0; i < totalePostazioni; i++)


if ( v[i] != NULL )
if ( strcmp(v[i], id) == 0 )
return false;

for (int j = 0; j < totalePostazioni; j++)


if ( v[j] == NULL ){
v[j] = new char[strlen(id)+1];
strcpy(v[j], id);
quanteOccupate++;
return true;
}
}

void Aula::elimina(int k){


k--;
if ( k < 0 || k >= totalePostazioni )
return;
if (v[k] != NULL){
delete []v[k];
v[k] = NULL;
quanteOccupate--;
}
}

// --- SECONDA PARTE ---

Aula::Aula(const Aula& p){


totalePostazioni = p.totalePostazioni;
quanteOccupate = p.quanteOccupate;
v = new char* [totalePostazioni];
for(int i = 0; i < totalePostazioni; i++) {
if ( p.v[i] == NULL )
v[i] = NULL;
else{
v[i] = new char[strlen(p.v[i])+1];
strcpy(v[i], p.v[i]);
}
}
}

Aula::~Aula(){
for(int i = 0; i < totalePostazioni; i++)
if ( v[i] != NULL )
delete []v[i];
delete []v;
}

Aula& Aula::operator!(){
if ( quanteOccupate == totalePostazioni ){
for( int i = 0; i < totalePostazioni; i++)
for( int j = totalePostazioni-1; j > i; j--)
if ( strcmp(v[j], v[i]) < 0 ){
char *aux = v[i];
v[i] = v[j];
v[j] = aux;
}
}
return *this;
}
FONDAMENTI DI INFORMATICA I PROVA PRATICA 8 GIUGNO
2016
CORSO DI LAUREA IN INGEGNERIA INFORMATICA

Smistacasse è un sistema che gestisce l’allocazione alle casse dei clienti di un supermercato. Ogni
cliente viene identificato univocamente da un numero intero diverso da zero e dal numero di articoli
acquistati. Le casse sono numerate a partire da 1. Una cassa può essere aperta o chiusa. Il numero di articoli
in coda ad una cassa è pari alla somma degli articoli dei clienti in coda a quella cassa.
L’allocazione dei clienti alle casse viene scelta in base alla cassa con il minimo numero di articoli in coda
in modo da minimizzare i tempi di attesa dei clienti.
Implementare le seguenti operazioni che possono essere fatte su Smistacasse:
--- PRIMA PARTE --- (qualora siano presenti errori di compilazione, collegamento o esecuzione in questa
parte, l’intera prova sarà considerata insufficiente e pertanto non sarà corretta)

 Smistacasse s(N);
Costruttore che crea un sistema Smistacasse con N casse (>=1). Tutte le casse sono aperte. Le code di tutte
le casse sono vuote.

 s.trovaCassa();
Operazione che ritorna il numero della prima cassa col minor numero di articoli in coda.

 s.aggiungi(id, n);
Operazione che aggiunge il cliente avente identificatore id, con numero di articoli acquistati n, al
sistema s. Non possono esistere clienti nel sistema con lo stesso identificatore.

 cout << s;
Operatore di uscita per il tipo Smistacasse. L’uscita per un sistema Smistacasse con 3 casse, dopo
l’arrivo del cliente con ID=68 con 11 articoli e del cliente con ID=74 con 7 articoli ha il seguente
formato:
1: (ID=68, ARTICOLI=11).
2: (ID=74, ARTICOLI=7).
3: <vuota>
L’uscita, dopo l’arrivo di altri due clienti (ID=76 con 8 articoli e ID=52 con 9 articoli, rispettivamente) ha
il seguente formato:
1: (ID=68, ARTICOLI=11).
2: (ID=74, ARTICOLI=7)(ID=52, ARTICOLI=9).
3: (ID=76, ARTICOLI=8).
All’arrivo del terzo cliente la terza cassa viene impegnata, infine il quarto cliente viene messo in coda alla
seconda cassa perchè quella con meno articoli da battere.

 s.servi(nCassa);
Operazione che serve il primo cliente in coda alla cassa nCassa. Lascia lo Smistacasse invariato qualora
non ci siano clienti in coda a quella cassa.
--- SECONDA PARTE ---

 s-=(nCassa);
Operazione che chiude la cassa nCassa. I clienti in coda presso quella cassa devono essere smistati tra le
casse rimanenti con la stessa politica utilizzata per l’arrivo di un nuovo cliente.
La stampa a video di una cassa chiusa ha il seguente formato:
1: <chiusa>
2: (ID=52, ARTICOLI=9).
3: (ID=68, ARTICOLI=11).

 ~Smistacasse();
Distruttore.

Mediante il Linguaggio C++, realizzare il tipo di dato astratto Smistacasse, definito dalle precedenti
specifiche. Gestire le eventuali situazioni di errore.

USCITA CHE DEVE PRODURRE IL PROGRAMMA

--- PRIMA PARTE ---


Test del costruttore e dell'operatore di uscita
1: <vuota>
2: <vuota>
3: <vuota>

Test della 'aggiungi'


1: (ID=68, ARTICOLI=11).
2: (ID=74, ARTICOLI=7).
3: <vuota>

1: (ID=68, ARTICOLI=11).
2: (ID=74, ARTICOLI=7)(ID=52, ARTICOLI=9).
3: (ID=76, ARTICOLI=8).

Test della 'trovaCassa'


3

Test della 'servi' sulla seconda e terza cassa


1: (ID=68, ARTICOLI=11).
2: (ID=52, ARTICOLI=9).
3: <vuota>

--- SECONDA PARTE ---


Test dell'operatore -= sulla prima cassa
1: <chiusa>
2: (ID=52, ARTICOLI=9).
3: (ID=68, ARTICOLI=11).

Test del distruttore (s1 e' stata appena distrutta)


#include <iostream>
#include <cstring>
using namespace std;

struct elem {
int id;
int articoli;
elem* pun;
};

enum stato {
APERTA, CHIUSA
};

class Smistacasse {
int _N;
elem** _casse;
stato* _stato;

friend ostream& operator<<(ostream&, const Smistacasse&);

public:
Smistacasse(int);
int trovaCassa();
void aggiungi(int, int);
void servi(int);

// --- SECONDA PARTE ---


Smistacasse& operator-=(int);
~Smistacasse();
};
cpp
#include "compito.h"

Smistacasse::Smistacasse(int N) {

( N < 1 ) ? _N = 4: _N = N;
_casse = new elem*[_N];
_stato = new stato[_N];
for (int i = 0; i < _N; i++) {
_casse[i] = NULL;
_stato[i] = APERTA;
}

int Smistacasse::trovaCassa(){

int min;
int cassaTrovata;

// trova la prima cassa non CHIUSA per inizializzare min e cassaTrovata


for(int i = 0; i < _N; i++)
{
if ( _stato[i] == APERTA )
{
int totArticoli = 0;
elem *p = _casse[i];
while (p!= NULL) {
totArticoli += p->articoli;
p = p->pun;
}
min = totArticoli;
cassaTrovata = i;
break;
}
}
for(int i = 0; i < _N; i++)
{
if ( _stato[i] == CHIUSA ) // escludi casse bloccate
continue;

// conta totale articoli in questa cassa


int totArticoli = 0;
elem *p = _casse[i];
while (p!= NULL) {
totArticoli += p->articoli;
p = p->pun;
}

if (totArticoli < min) {


// trovata una cassa migliore
min = totArticoli;
cassaTrovata = i;
}
}

return (cassaTrovata + 1);


}

void Smistacasse::aggiungi(int id, int articoli){

if (id <= 0 || articoli <= 0)


return;

int numCassa = trovaCassa();


if (_stato[numCassa-1] == CHIUSA)
return;

elem* r = new elem;


r->id = id;
r->articoli = articoli;
r->pun = NULL;

elem* p = _casse[numCassa-1];
elem* q = NULL;

while (p!=NULL) {
q = p;
p = p->pun;
}

if (q==NULL) // caso lista vuota


_casse[numCassa-1] = r;
else
q->pun = r;
}

void Smistacasse::servi(int numCassa){

if ( numCassa <= 0 || numCassa > _N )


return;

elem *p = _casse[ numCassa-1 ];


elem *q;
if (p!=NULL) {
q = p;
_casse[ numCassa-1 ] = p->pun; // aggiorna la testa
delete q;
}
}

ostream& operator<<(ostream& os, const Smistacasse& s){


for( int i = 0; i < s._N; i++ ){
os <<i+1<<": ";
if ( s._stato[i]==CHIUSA ) {
os <<"<chiusa>"<<endl;
}
else {
if (s._casse[i]==NULL)
os <<"<vuota>"<<endl;
else {
elem* q;
for( q = s._casse[i]; q != NULL; q = q->pun){
os << "(ID=" << q->id << ", ARTICOLI=" << q->articoli << ")";
}
os << "."<<endl;
}
}
}
return os;
}

// --- SECONDA PARTE ---

Smistacasse& Smistacasse::operator-=(int n){

if (n <= 0 || n > _N)


return *this;

// blocca la cassa
_stato[n-1] = CHIUSA;

// ri-distribuisci clienti
elem* p = _casse[n-1];
elem* r;
while( p != NULL ){
r = p;
aggiungi(r->id, r->articoli);
p = p->pun;
delete r;
}
_casse[n-1] = NULL;

return *this;
}

Smistacasse::~Smistacasse(){
elem* r;
elem* p;
for(int i = 0; i < _N; i++){
p = _casse[i];
while( p != NULL ){
r = p;
p = p->pun;
delete r;
}
}
delete[] _casse;
delete[] _stato;
}