Questa pagina descrive il progetto per il corso di Informatica della Laurea in Matematica per gli appelli dell'anno accademico 2021/2022.
Il progetto consiste nello sviluppo di un interprete per un semplice linguaggio di programmazione imperativo, SuperBasic, che descriveremo sotto. L'interprete (scritto in Java) dovrà aprire un file di testo contenente il codice di un programma (scritto nel linguaggio SuperBasic), e simularne l'esecuzione.
Un programma in SuperBasic ha la seguente forma
NV NL MS linea0 linea1 linea2 ... lineaNL-1
dove la prima riga contiene tre numeri naturali NV NL NS
che hanno il seguente significato.
NV
indica il numero di variabili usate dal programma.
Il programma userà esclusivamente come nomi di variabili i nomi
x0 x1 x2 ... xA
dove A=NV-1
.
I valori che le variabili possono assumere sono gli stessi interi
assunti dagli int
di Java.
Ogni programma usa almeno tre variabili x0,x1,x2
quindi
si ha NV >= 3
.
NL
indica il numero di linee di codice del
programma. A partire dalla seconda riga del programma, si trovano
esattamente NL
linee, ognuna contenente un comando.
Numereremo queste linee a partire da zero, e quindi chiameremo
"linea 0" la seconda riga del programma,
"linea 1" la terza, e così via, come abbiamo già indicato sopra.
MS
indica la dimensione massima dello stack,
una struttura dati che useremo durante l'esecuzione del programma.
Potete rappresentare lo stack come un vettore di interi di lunghezza
MS
.
L'esecuzione del programma può portare o a un valore intero
risultato, memorizzato nella variabile x2
o a un errore
in fase di esecuzione. L'interprete dovrà riportare nel suo output
se l'esecuzione è terminata con successo (e con quale risultato), o
se invece c'è stato un errore (e quale).
Ogni linea del programma contiene un comando in SuperBasic, spiegati in dettaglio sotto. I comandi di SuperBasic sono:
let xA = K let xA = xB OP xCper assegnare valori a variabili.
if ____ goto _comandi di salto condizionale. Se la condizione è verificata, l'esecuzione salta alla linea indicata dal
goto
.
while ____ exit _comandi per l'esecuzione di cicli. Se la condizione è vera, vengono eseguite le istruzioni successive finché non viene trovato un
next
, altrimenti, se la condizione è
falsa, l'esecuzione salta alla linea indicata
dall'exit
.
nextper tornare all'ultimo while eseguito.
rem ciao sono un bellissimo commentonon hanno alcun effetto sull'esecuzione.
endper concludere l'esecuzione e restituire il valore di
x2
come valore di output.
L'esecuzione di un programma SuperBasic inizia
dalla linea0
. Dopo l'esecuzione di una linea di codice,
salvo dove diversamente previsto dal comando, si continua con la linea
sottostante.
Tutte le variabili usate in SuperBasic sono intere e hanno lo stesso
comportamento degli int
di Java. All'inizio
dell'esecuzione, le variabili x0,x1
sono inizializzate
con dei valori di input (vedi sotto), mentre le altre non sono inizializzate.
Alla fine dell'esecuzione la
variabile x2
contiene invece un valore di output.
Le variabili diverse da x0,x1
non possono essere lette
prima della loro inizializzazione (prima che si assegni un valore a
esse).
All'interprete del codice SuperBasic viene fornito un
naturale timeout
che indica il numero massimo di linee
di codice da eseguire, superato il quale il programma viene
forzatamente terminato (vedi sotto).
L'esecuzione di un programma SuperBasic può causare degli errori. Oltre ai possibili errori degli specifici comandi (vedi sotto), il programma può causare alcuni errori generali:
Possibili errori generali
timeout
senza terminare col
comando end
. In questo caso, il programma viene
forzosamente terminato e l'output dovrà essere la stringa
"ERROR: execution timeout"
.lineaNL
(che non esiste),
senza arrivarci con un salto (if, while
): in questo caso,
l'output dovrà essere la stringa "ERROR: max code line exceeded"
"ERROR: syntax error in line N"
, dove N
è la linea
corrente del programma.let xA = K
dove A
è un numero naturale
mentre K
è una costante intera.
L'effetto del comando è quello di assegnare alla variabile xA
l'intero K
.
Possibili errori:
A >= NV
): in questo caso, l'output dovrà essere la stringa
"ERROR: access to non existing variable in line N"
, dove
N
è la linea corrente del programma.let xA = xB OP xC
dove A,B,C
sono numeri naturali
mentre OP
è un operatore tra +,-,*,/
.
Come in Java, l'operatore /
indica il quoziente tra due
interi.
L'effetto del comando è quello di assegnare alla variabile xA
il risultato dell'operazione OP
tra le variabili menzionate.
Nota bene: l'operazione viene eseguita necessariamente fra due variabili! NON è possibile eseguire un comando come ad esempio
let x2 = x1 + 2
il quale dovrà causare l'errore "ERROR: syntax error in line N"
,
dove N
è la linea corrente del programma.
Possibili errori:
"ERROR: access to non existing variable in line N"
, dove
N
è la linea corrente del programma.xB
e xC
(ossia quelle di cui si sta cercando
di leggere il valore) non è mai stata inizializzata:
in questo caso, l'output dovrà essere la stringa
"ERROR: uninitialized variable read in line N"
, dove
N
è la linea corrente del programma."ERROR: division by 0 in line N"
, dove
N
è la linea corrente del programma.if xA OP xB goto N if xA OP xB goto +N if xA OP xB goto -N
dove A,B,N
sono numeri naturali
mentre OP
è un operatore di confronto tra
==,!=,<,<=,>,>=
.
L'effetto del comando è quello di controllare la guardia
espressa tra le due variabili.
Se la guardia NON vale, il programma prosegue dalla riga
sottostante.
Se invece la guardia vale, l'esecuzione del programma
prosegue dalla riga di codice
L
, definita come segue:
goto N
, la linea L
è
N
stesso.goto +N
, la linea L
è
quella N
linee sotto la riga corrente.goto -N
, la linea L
è
quella N
linee sopra la riga corrente.
Nota bene: la guardia è espressa tra due variabili. Analogamente a quanto
avviene per il comando let
, NON è possibile effettuare
confronti con costanti. Ad esempio, il comando
if x0 < 2 goto +2
dovrà restituire la stringa "ERROR: syntax error in line M"
,
dove M
è il numero di linea corrente.
Possibili errori:
"ERROR: access to non existing variable in line M"
, dove
M
è la linea corrente del programma.xA
e xB
(ossia quelle di cui si sta cercando
di leggere il valore) non è mai stata inizializzata:
in questo caso, l'output dovrà essere la stringa
"ERROR: uninitialized variable read in line M"
, dove
M
è la linea corrente del programma.L
di destinazione del goto non è una
linea di codice valida, nel senso che o è un intero negativo
(L < 0
), o è oltre il numero di linee
del programma (L >= NL
):
in questo caso, l'output dovrà essere la stringa
"ERROR: invalid goto destination in line M"
, dove
M
è la linea corrente del programma.
Questo errore viene generato anche se la guardia è falsa e non
si salta alla linea L
.
while xA OP xB exit N while xA OP xB exit +N while xA OP xB exit -N
dove A,B,N
sono numeri naturali
mentre OP
è un operatore di confronto tra
==,!=,<,<=,>,>=
.
L'effetto del comando è quello di controllare la guardia espressa tra
le due variabili.
Se la guardia NON vale, l'esecuzione del programma prosegue
dalla riga L
, definita in modo analogo a quanto fatto per
il comando if .. goto ..
.
Se invece la guardia vale, la riga di codice corrente viene aggiunta
alla cima dello stack, e l'esecuzione del programma prosegue dalla
riga di codice sottostante.
Nota bene: la guardia è espressa tra due variabili. Analogamente a quanto
avviene per il comando if ... goto ...
, NON è possibile effettuare
confronti con costanti. Ad esempio, il comando
while x0 < 2 exit +2
dovrà restituire la stringa "ERROR: syntax error in line M"
,
dove M
è il numero di linea corrente,
Possibili errori:
"ERROR: access to non existing variable in line M"
, dove
M
è la linea corrente del programma.xA
e xB
(ossia quelle di cui si sta cercando
di leggere il valore) non è mai stata inizializzata:
in questo caso, l'output dovrà essere la stringa
"ERROR: uninitialized variable read in line M"
, dove
M
è la linea corrente del programma.L
di destinazione dell'exit non è una
linea di codice valida, nel senso che o è un intero negativo
(L < 0
), o è oltre il numero di linee
del programma (L >= NL
):
in questo caso, l'output dovrà essere la stringa
"ERROR: invalid exit destination in line M"
, dove
M
è la linea corrente del programma.
Questo errore viene generato anche se la guardia è vera e non
si salta alla linea L
.
MS
elementi)):
in questo caso, l'output dovrà essere la stringa
"ERROR: stack overflow executing line M"
, dove
M
è la linea corrente del programma.next
L'effetto del comando è quello di riprendere l'esecuzione del programma
dall'ultimo while
eseguito e aggiunto sullo stack. Per questo, viene
estratto un numero di riga L
dalla cima dello stack,
e l'esecuzione del programma dovrà proseguire da tale riga.
Possibili errori:
"ERROR: stack underflow executing line N"
, dove
N
è la linea corrente del programma.rem TESTO
dove TESTO
è una riga di testo arbitraria.
Il comando non ha alcun effetto.
Possibili errori: nessuno.
end
Il comando termina il programma, restituiendo come valore
finale quello della variabile x2
. Se non ci sono
errori (vedi sotto), a questo punto l'output dovrà restituire la stringa
"OK: V"
, dove
V
è il valore della variabile x2
.
Possibili errori:
x2
non è mai stata inizializzata:
in questo caso, l'output dovrà essere la stringa
"ERROR: uninitialized variable read in line N"
, dove
N
è la linea corrente del programma.Può capitare che più errori si verifichino contemporaneamente durante l'esecuzione del programma. La precedenza degli errori è data dall'ordine in cui sono stati presentati sopra, nei concetti generali e nell'elenco comandi. Gli errori generali hanno sempre la precedenza sugli errori dello specifico comando in esecuzione.
Alcuni esempi:
"ERROR: execution timeout"
, indipendentemente da cosa
si trova nella riga corrente. Per esempio, se stiamo eseguendo il programma
3 2 2 rem linea0 rem linea1ed il parametro di timeout è impostato a 2, l'output corretto sarà
"ERROR: execution timeout"
, e NON
"ERROR: max code line exceeded"
. Analogamente, se stiamo eseguendo il programma
3 3 2 rem linea0 rem linea1 linea con una sintassi sbagliataed il parametro di timeout è impostato a 2, l'output corretto sarà
"ERROR: execution timeout"
, e NON
"ERROR: syntax error in line 2"
. 3 1 2 let x1 = x2 + 2l'output corretto sarà
"ERROR: syntax error in line 0"
, e NON
"ERROR: uninitialized variable read in line 0"
. 3 1 2 let x1 = x2 + x4l'output corretto sarà
"ERROR: access to non existing variable in line 0"
, e NON
"ERROR: uninitialized variable read in line 0"
.
Per gestire il comando while ... exit ...
e il
comando next
è necessario utilizzare uno stack
(chiamato anche pila). Intuitivamente, si tratta di una
struttura dati che può essere visualizzata come una sequenza di
interi A0 A1 A2 ... Ak
.
Su uno stack possiamo fare due operazioni: aggiungere valori sulla
cima della pila (aggiungendo un intero dopo l'ultimo della sequenza)
ed estrarre il valore in cima alla pila (rimuovendo l'ultimo intero
dalla sequenza). Per rappresentare lo stack, potete usare un
vettore di interi di lunghezza
MS
insieme a una variabile di tipo int
con
cui tenete traccia della posizione della cima della pila. Gli
interi del vettore dalla posizione 0 fino alla posizione della cima
(esclusa) rappresentano gli interi delle sequenza rappresentata dallo stack,
mentre il valore degli altri interi del vettore è irrilevante.
L'idea è che lo stack serve per tenere in memoria il numero di linea
dei cicli while
che sono ancora in esecuzione, in
maniera da poter riportare l'esecuzione del programma alla riga
corretta nel momento in cui si incontra il
comando next
.
Ad esempio, se vogliamo eseguire il seguente programma con gli input
x0 = 0
e x1 = 0
:
3 4 3 while x0 == x0 exit 1 while x1 == x0 exit 3 let x1 = 1 next
Dovremo allocare un vettore di interi di lunghezza 3
con cui
rappresentare lo stack:
_ _ _
All'inizio lo stack sarà vuoto. Quando l'esecuzione raggiunge la linea 0
,
poiché la guardia è vera dobbiamo salvare la linea di codice corrente sullo stack,
in modo da sapere da dove riprendere l'esecuzione quando incontriamo un'istruzione
next
.
0 _ _
Eseguendo poi la linea 1
, di nuovo la guardia è vera, quindi dobbiamo salvare
la linea di codice corrente sullo stack, in modo da poterci ritornare quando
incontriamo un'istruzione next
0 1 _
A questo punto in riga 3
incontriamo un'istruzione next
.
Questo significa che dobbiamo riprendere l'esecuzione dall'ultimo while visitato. Il
numero di riga dell'ultimo while
visitato è contenuto in cima allo stack
(ed è 1
), quindi lo estraiamo dallo stack. A questo punto lo stato dello
stack sarà il seguente
0 _ _
e l'esecuzione riprende da linea 1
. Osservate che in questo momento, se
incontrassimo un'altra istruzione next
, l'esecuzione ritornerebbe alla
posizione dell'ultimo while
visitato, che a questo punto è linea 0
.
Il while
in linea 1
è stato "chiuso" dal next
che
abbiamo appena incontrato.
Proseguendo con l'esecuzione del nostro programma (quindi da linea 1
), abbiamo
un'istruzione while
, la cui guardia ora è falsa (x0 = 1
e
x1 = 0
), pertanto l'esecuzione salta alla linea di codice indicata da
exit
, ossia linea 3
, e sullo stack non viene fatto nulla.
A questo punto troviamo un'altra istruzione next
, quindi viene estratta
dallo stack la posizione dell'ultimo while incontrato (in linea 0
). Lo
stato dello stack a questo punto sarà il seguente
_ _ _
e l'esecuzione riprenderà da linea 0
.
In questo esempio il while esterno continuerà a eseguire per sempre, fino a quando
non si raggiunge l'errore di timeout.
Sotto trovate altri esempi di programmi SuperBasic.
Si chiede di scrivere una funzione Java avente (esattamente) nome e tipo come segue:
public static String semantica(String inputFile, int in0, int in1, int timeout)
Il parametro inputFile
indica un nome di un file
da cui leggere, contenente il codice del programma SuperBasic da eseguire.
I parametri in0
e in1
indicano i valori di input
del programma, a cui saranno inizializzate le variabili x0
e x1
a inizio esecuzione.
Il parametro timeout
indica il numero massimo di linee di codice
che saranno eseguite prima che venga terminata l'esecuzione del programma con
un errore di timeout.
L'output della funzione semantica(...)
dovrà essere una
stringa, il cui contenuto è il risultato dell'esecuzione del
programma, sia esso uno degli errori descritti sopra o il risultato
ottenuto terminando l'esecuzione con end
("OK: V"
).
Quando la funzione viene chiamata, vi viene garantito che il file di
input inputFile
è un file di testo con esattamente
la forma seguente:
NV NL MS linea0 linea1 linea2 ... lineaNL-1
Nella prima riga del file troviamo le costanti NV NL MS
che sono i parametri del programma (già descritti sopra).
Dalla seconda riga in poi troviamo NL
linee di testo arbitrarie,
da interpretare secondo la semantica informale descritta sopra.
Si noti che non è garantito che tali linee contengano comandi nella sintassi data sopra, nel qual caso l'interprete deve generare un errore di sintassi non appena una linea invalida viene eseguita.
In caso di input malformato non corrispondente a quanto garantito
sopra (per esempio, NV
non è un naturale, o
NL
non corrisponde al numero di linee sottostante nel file)
non si richiede nessun comportamento particolare. Il
vostro programma Java può dare un risultato qualunque, o anche generare un
errore a tempo di esecuzione.
Per sommare N+(N-1)+...+1
in Java scriveremmo così:
// x0 contiene N, in x2 mettiamo la somma x2 = 0; while (x0 > 0) { x2 = x2 + x0; x0 = x0 - 1; }
Traducendo il codice di sopra in SuperBasic, potremmo scrivere:
5 11 1 rem x0 contiene N, in x2 mettiamo la somma rem le costanti 0 e 1 le mettiamo in x3 e x4 let x3 = 0 let x4 = 1 rem inizializziamo x2 let x2 = 0 while x0 > x3 exit +4 let x2 = x2 + x0 let x0 = x0 - x4 next end
Sull'input di sopra, su input x0=5,x1=0
e timeout
sufficientemente grande, va generato come output OK: 15
.
Dati x0
e x1
verifichiamo se x0
è
il quadrato di un numero minore di x1
. In Java scriveremmo:
y = 0; x2 = 0; while (y < x1) { if (y*y == x0) { x2 = 1; } y++; }
In questo modo, alla fine x2
sarà rimasto 0 se e solo se x0
non è uno dei quadrati, e sarà diventato 1 altrimenti.
Traducendo il codice di sopra in SuperBasic, potremmo scrivere:
6 13 1 rem la costante 1 la metto in x3 let x3 = 1 rem y la chiamo x4 let x4 = 0 let x2 = 0 while x4 < x1 exit +7 rem metto il quadrato in x5 let x5 = x4 * x4 if x5 != x0 goto +2 let x2 = 1 let x4 = x4 + x3 next end
Dato x0
, contiamo quante triple pitagoriche
a*a = b*b + c*c
ci sono con 1 <= a,b,c < x0
.
In Java scriveremmo:
x2 = 0; a = 1; while (a < x0) { b = 1; while (b < x0) { c = 1; while (c < x0) { if (a*a == b*b + c*c) { x2++; } c++; } b++; } a++; }
In SuperBasic scriveremmo:
11 25 3 rem la costante 1 la mettiamo in x3 let x3 = 1 rem le variabili a,b,c le chiamiamo x4,x5,x6 let x2 = 0 let x4 = 1 while x4 < x0 exit +19 let x5 = 1 while x5 < x0 exit +15 let x6 = 1 while x6 < x0 exit +11 rem i quadrati in x7,x8,x9 let x7 = x4 * x4 let x8 = x5 * x5 let x9 = x6 * x6 rem la somma dei quadrati in x10 let x10 = x8 + x9 if x7 != x10 goto +2 let x2 = x2 + x3 let x6 = x6 + x3 next let x5 = x5 + x3 next let x4 = x4 + x3 next end
Ricordatevi che, quando confrontate due String
in Java
dovete usare s1.equals(s2)
e non s1 == s2
.
Potrebbe essere utile usare i comandi
s.split(...), s.charAt(...), s.substring(...)
per manipolare le stringhe.
Potrebbe essere utile anche ricordare che il metodo
Integer.parseInt(..)
può generare un'eccezione di tipo
NumberFormatException
se la stringa che viene passata non è
un intero (ad esempio Integer.parseInt("45ye2")
). Potete
gestire questa eccezione a vostro piacimento con un blocco
try { ... } catch (NumberFormatException e) { ... }
Per gestire le variabili non inizializzate si può usare un vettore di booleani.
Si chiede di scrivere una singola classe Java Progetto
,
che contenga la funzione semantica
descritta
sopra. In particolare, è
obbligatorio usare come base per il
progetto un progetto Eclipse contenente il seguente scheletro di
di classe Java.
La classe scheletro va posizionata in un progetto Eclipse come segue.
Create un progetto Eclipse chiamato cognome_matricola
(usate i vostri dati, per esempio
tramaglino_000001
o
de_paperoni_000002
),
e create la classe Progetto
al suo interno nella cartella
src
. Dovete ottenere un file come segue:
zunino_555555/src/Progetto.java
È obbligatorio rispettare la posizione del file della classe come descritto sopra. Dentro quel file potete quindi inserire il codice dello scheletro fornito.
È tassativamente proibito
modificare lo scheletro
nelle parti segnate al suo interno come da non modificare.
In particolare, non si può modificare il tipo o il nome delle
procedure / funzioni già presenti all'interno.
In nessun caso il
file consegnato dovrà contenere una dichiarazione
package
.
È invece consentito (e consigliabile) definire delle
procedure/funzioni addizionali all'interno della
classe Progetto
. È consentito aggiungere
import
per usare le librerie standard di Java.
È consentito modificare il metodo main
della classe
nello scheletro. Si noti, tuttavia, che tale metodo
potrà essere cancellato e sovrascritto, o comunque
non eseguito, da chi corregge.
Di conseguenza, se si decidono di usare variabili globali,
queste devono essere inizializzate dentro la funzione
semantica
, e non dentro
il main
, in quanto quest'ultimo non verrà eseguito.
Durante la correzione, la funzione semantica
verrà chiamata ripetutamente eseguendo (anche) test automatizzati.
Per potere testare più facilmente il vostro progetto, vi viene fornita la seguente libreria Java e un archivio di test. L'uso di questa libreria è opzionale.
Dopo avere scaricato il file helper di sopra in una qualunque cartella,
potete inserire la libreria helper nel vostro progetto Eclipse,
selezionando il vostro progetto,
facendo clic destro,
e selezionando dal menù la voce
Build Path -> Add External Archives...
.
Comparirà un finestra dove potete selezionare
il file jar
fornito sopra.
Dopo avere aggiunto la libreria helper al vostro progetto,
all'interno del file Java Progetto.java
potete usare le funzioni della libreria, che mostriamo sotto.
Nello scheletro di progetto che vi forniamo,
trovate già degli esempi su come usarle nel main()
,
dopo avere usato import progetto2021.Helper;
all'inizio del file.
static boolean Helper.checkSemantica(String inputFile, int in0, int in1, int timeout, String yourOutput)
La funzione Helper.checkSemantica
prende come
parametri gli stessi parametri della funzione semantica
,
più un parametro addizionale String yourOutput
che rappresenta
l'output della funzione semantica
che vogliamo controllare.
La funzione Helper.checkSemantica
restituisce il
booleano true
quando yourOutput
coincide col
risultato atteso dell'esecuzione del programma SuperBasic, e
false
altrimenti. In caso l'output non coincida, la
funzione stampa a video un messaggio con l'output atteso, in modo da
aiutarvi nella correzione del vostro programma.
Usare l'helper non è obbligatorio (anche se fortemente raccomandato).
Se non lo volete usare, per potere compilare lo scheletro
dovete rimuovere dal file sia la riga
import progetto2021.Helper;
che tutti i riferimenti alle funzioni Helper
.
Nota bene: se usate l'helper su tutti i test forniti, e se su questi
test la vostra funzione semantica
fornisce il risultato
atteso, questo non vuol dire che la funzione sia corretta in tutti i
casi possibili. È possibile che, in fase di valutazione del progetto,
vengano controllati altri casi, e che su questi si scopra che il
vostro codice non funziona correttamente. In altre parole: con i test
possiamo solo dimostrare che un programma è scorretto, ma mai che è
corretto.
Il progetto verrà valutato secondo i seguenti criteri, che descrivono un insieme di requisiti sul codice consegnato. Ogni requisito primario deve essere rispettato rigidamente: in caso di violazione, anche minima, di uno di questi requisiti la prova d'esame non è superata. I requisiti secondari devono essere comunque rispettati. Violazioni minori saranno tollerate, ma violazioni gravi possono comunque causare il non superamento della prova.
Requisiti tecnici (primario). La soluzione consegnata deve seguire lo scheletro di progetto fornito sopra, modificato nel modo descritto sopra.
Devono inoltre essere rispettati questi requisiti:
Progetto
, i nomi o i tipi delle procedure/funzioni
segnate come da non modificare.
Non ci deve essere nessuna dichiarazione di
package
della classe.
Progetto
come descritto
sopra nel vostro progetto Eclipse.
new File(fileName)
usa il parametro, mentre
new File("fileName")
usa un nome prefissato.
main
invece può interagire con l'utente, se desiderato
(ma comunque come detto sopra il codice del main
può essere ignorato da noi durante i test.)
Il non rispettare questi requisiti tecnici può causare il fallimento dei nostri test automatizzati, ed in tal caso la prova non sarà superata.
Correttezza (primario).
Non ci devono essere errori a tempo di compilazione: il programma deve
compilare. Non ci devono essere errori a tempo di esecuzione: il
programma non deve generare eccezioni (per
esempio, DivisionByZero
oppure ArrayIndexOutOfBounds
) quando eseguito su un input
ben formato. Il programma deve dare l'output desiderato su qualunque
input compatibile con la specifica.
Nota bene:
Nel caso il programma sia scorretto, non è compito di chi corregge
fare il debugging,
ovvero identificare la causa dell'errore e suggerire una modifica
per rimuoverla. Anche quando tale causa fosse nota a chi corregge,
non verrà comunicata allo studente, in quanto sarebbe come suggerire
una parte non banale della soluzione della prova. Infatti, capita
frequentemente che correggere l'errore dopo averlo
individuato diventi banale ("devo usare x
, e non
x+1
"), e che la prova di conseguenza consista prevalentemente
nella ricerca dell'errore.
Corollario:
se provando il programma su un insieme di input campione si riscontrano
già errori di correttezza, chi corregge non è tenuto ad esaminare
il codice del programma.
Leggibilità (secondario). Il codice deve essere scritto in modo tale da permettere a un altro programmatore di comprenderne la logica. Non è sufficiente che il codice "funzioni", o che sia chiaro a chi lo ha scritto. Per aiutare la lettura del codice da parte di altri, si consiglia di usare dei nomi di variabile e di funzione appropriati, strutturare il codice adeguatamente (dove ha senso, meglio dividere una funzione lunga in più funzioni ausiliarie), e di inserire dei commenti.
Non commentate come state calcolando qualcosa, commentate piuttosto cosa state calcolando. Per esempio, il seguente commento è altamente inutile:
// incremento i i++;
Al contrario, il seguente aiuta a comprendere il codice:
// passo a coordinate polari rho = Math.sqrt(x*x + y*y); theta = Math.atan2(y, x);
Efficienza (secondario).
Il vostro programma deve svolgere il suo compito in tempi ragionevoli.
Noi dobbiamo essere in grado di svolgere agevolmente i nostri test
chiamando la funzione semantica
molte volte anche
su input complessi.
Se il vostro programma è così lento da impedirci di eseguire tutti i test nel tempo di pochi minuti, riterremo tutti i test falliti.
Per potere partecipare allo scritto di un appello di esame, il progetto deve essere consegnato entro le date indicate nell'elenco degli appelli d'esame. La consegna si svolge su Moodle, in modo simile a quanto fatto per il tutorato.
zunino_555555/src/Progetto.java
zunino_555555/README.txt
Se avete dubbi sul testo del progetto, ovvero su che cosa vi sta venendo chiesto, chiedete pure delucidazioni per email o a ricevimento. Se invece avete dubbi sulla soluzione dell'esercizio, ovviamente non possiamo suggerirvi nulla.
Il progetto non viene svolto in un ambiente "controllato" come per esempio avviene per un esame scritto, ma vi viene lasciata libertà di svolgerlo dove e quando preferite (compatibilmente con le scadenze). Per esempio, potete usare i laboratori quando liberi, o farlo su un vostro computer personale.
Il progetto è individuale, non di gruppo. Tuttavia, vi è consentito discutere del progetto con altre persone per scambiarsi opinioni a riguardo. Non è consentita, ovviamente, la copia di pezzi di codice inerenti al progetto da uno studente all'altro. Allo stesso modo, farsi fare il progetto da un'altra persona è considerato equivalente a copiare. In caso di dubbi sull'autenticità del progetto, ci riserviamo la possibilità di convocarvi per un colloquio sul codice che avete consegnato, anche dopo l'esame scritto.
Vi è consentito di "copiare" invece pezzi di codice che vi abbiamo fornito noi, o di "tradurre in Java" dei pezzi di codice che potreste trovare su qualche libro o tutorial online, e che non siano soluzioni degli esercizi proposti sopra o di una loro parte significativa. Nei casi consentiti dovete obbligatoriamente citare la fonte in un commento nel codice.
Il discutere del progetto su forum di discussione su Internet o simili non è vietato a prescindere, ma è soggetto alle stesse regole della comunicazione tra altri studenti. Inoltre, se iniziate una discussione su un forum riguardo al progetto, dovete obbligatoriamente dichiarare 1) che l'esercizio in questione è un progetto di esame, e 2) che non desiderate che qualcuno vi scriva una soluzione al posto vostro. Se anche chiarendo ciò qualcuno vi risponde includendo del codice, voi non potete includerlo nel progetto.
Se preferite, potete usare anche caratteristiche di Java non viste a lezione (costrutti diversi come gli oggetti, librerie non viste a lezione purché incluse tra quelle standard di Java), anche se il progetto si può svolgere benissimo senza. Dovete però comprendere il codice che state usando: se venite chiamati ad un colloquio (vedi sopra) vi può essere chiesto di spiegarlo.
Se ci sono parti del testo del progetto che ritenete non chiare, vi invitiamo a fare domande usando il forum che trovate su Moodle, in modo che tutti gli studenti possano leggere le nostre risposte. Allo stesso modo, per domande di interesse generale, vi invitiamo a usare il forum e non l'email.
Informatica - Teaching - Home
Roberto Zunino, 2021