L'obiettivo di questa esercitazione è familiarizzare con i seguenti concetti, illustrati nel capitolo 2 del libro:
%
"Scriverete anche i primi semplici programmi creati da voi. Gli esercizi etichettati con l'asterisco (*) sono più difficili: vi consiglio di affrontarli dopo aver risolto gli altri.
Obiettivo: Comprendere i meccanismi del ciclo editing-compilazione-esecuzione.
Attività:
CiaoATutti
e salvatelo in un
file di nome ciaoatutti.java (senza le lettere
maiuscole). Provate a compilare: che comando dovete usare? Provate poi
a eseguire: che comando dovete usare? Suggerimento:
usate il comando ls per vedere che file è stato
creato dal compilatore...Morale: usate sempre l'estensione
.java e salvate ogni programma in un file con lo
stesso "nome del programma" (quello che c'è dopo
class
) a cui aggiungere l'estensione
.java
. Inoltre, prestate particolare attenzione alle
lettere maiuscole, che sono diverse da quelle minuscole.
Obiettivo: Familiarizzare con le regole per gli identificatori e con i messaggi di errore del compilatore.
Attività:
class Identificatori { public static void main(String[] args) { int x; } }(in realtà è un programma estremamente povero, che non fa assolutamente nulla, ma per ora ci basta). Provate a modificare il nome dell'identificatore di variabile
x
in maniera non
consentita (rivedete le regole sintattiche per gli identificatori). Ad
esempio, provate in sequenza, a sostituire x
con
class
(che è una parola riservata), con
1x
(che comincia con un carattere non consentito), o con
x 1
("x spazio uno", che contiene carattere spazio, non
consentito). Ricompilate dopo ogni modifica e cercate di capire i
messaggi di errore del compilatore.Obiettivo: Familiarizzare con i tipi di dato primitivi.
Attività:
AreaTriangolo
visto a
lezione e modificate le dichiarazioni delle variabili
area
, base
e altezza
per far
sì che il programma funzioni correttamente anche se l'area non
è un valore intero.byte
,
short
, int
e long
(tabella 2.2
a pag. 33), scrivendo un breve programma in cui:
b
, s
, i
ed l
;System.out.println
) il valore
che è in realtà contenuto nelle variabili.class "NomeProgramma" { public static void main(String[] args) { "..." } }dove, al posto di "
NomeProgramma
" ci va il nome del
programma, e al posto dei puntini "...
" le istruzioni che
compongono il programma, in sequenza. In questo caso il programma
sarà (le parti fra virgolette "" o doppi apici, come dicono gli
informatici, sono quelle da completare):
/* Programma per verificare "..." */ class VerificaMassimiMinimi { public static void main (String[] args) { "dichiarazione delle 4 variabili" b = (byte)(127 + 1); s = (short)("..."); "..." "..." System.out.println(b); System.out.println(s); System.out.println(i); System.out.println(l); } }Fate attenzione che i letterali di tipo
long
vanno seguiti
da una L
. E non dimenticate i punti e virgola alla fine di
ogni istruzione! Obiettivo: Familiarizzare con i letterali e le sequenze di escape.
Attività:
char
e poi visualizza il
contenuto della variabile. Suggerimento Avrete
bisogno di un programma con 3 istruzioni: dovrete prima dichiarare la
variabile di tipo char
, poi assegnarle il valore voluto
usando la sequenza di escape e poi stamparla (usando la
System.out.println
). String
e poi
visualizza il contenuto della variabile (usando la
System.out.println
).Obiettivo: Familiarizzare con gli operatori fra valori interi.
Attività:
AreaTrapezio
che assegna dei valori a vostra scelta alle
variabili b1
, b2
e h
e calcola
l'area del trapezio corrispondente. Scrivete un'unica espressione,
usando le parentesi dove opportuno. Suggerimento:
Ispiratevi al programma AreaTriangolo
e adattatelo
(bastano minime modifiche).Obiettivo: Familiarizzare con l'operatore di modulo "%" (il resto della divisione intera).
Attività:
x
, di
tipo int
, dopo ognuno degli assegnamenti seguenti?
x = 16 / 2; x = 15 / 2; x = 16 % 2; x = 15 % 2;Scrivete un breve programma per verificare che la vostra risposta è corretta. Suggerimento: È sufficiente scrivere un programma contenente la dichiarazione della variabile
x
e poi i 4 assegnamenti a x
dei 4 valori,
ognuno seguito dall'istruzione di visualizzazione del valore di
x
(System.out.println
).
y
il valore 0 se la variabile intera
x
è pari, 1 se x
è
dispari. Scrivete un breve programma per verificare che la vostra
soluzione è corretta. Suggerimento: ripensate
al risultato delle ultime due operazioni dell'esercizio precedente e
completate (ed eseguite più volte su più valori di
x
) il programma seguente:
/* Programma per "..." */ class PariDispari { public static void main (String[] args) { int y; int x; x = "..."; // valore di prova y = "..."; //espressione da trovare... System.out.println(y); } }
Obiettivo: Familiarizzare con
il tipo di dato boolean
.
Attività:
boolean
è il più semplice di
tutti, dato che le variabili di quel tipo possono assumere solo i
valori true
e false
. Eppure spesso risulta
di difficile comprensione ai neofiti. Scrivete l'istruzione di
assegnamento che assegna alla variabile booleana p
il
valore true
se la variabile intera x
è pari, false
altrimenti. Scrivete un breve
programma per verificare che la vostra soluzione è
corretta. Suggerimento: ripensate all'esercizio
precedente e usate l'operatore di uguaglianza "==
" nella
parte destra dell'assegnamento. Fate attenzione: la modifica è
minima, ma richiede un salto logico...
p
il valore true
se la variabile
intera x
ha un valore positivo (maggiore o uguale a
zero), false
altrimenti. Scrivete un breve programma per
verificare che la vostra soluzione è
corretta. Suggerimento: Questo esercizio e quelli
immediatamente successivi sono varianti minime del precedente. p
il valore true
se la variabile
intera x
ha un valore positivo (maggiore o uguale a zero)
o pari, false
altrimenti. Scrivete un breve programma per
verificare che la vostra soluzione è corretta.
Suggerimento: usate gli operatori logici.p
il valore true
se la variabile
intera x
ha un valore positivo (maggiore o uguale a zero)
e pari, false
altrimenti. Scrivete un breve
programma per verificare che la vostra soluzione è
corretta.p
il valore true
se la variabile
intera x
ha un valore positivo compreso in uno dei
seguenti intervalli: [15,18], [39,45], [91,103], false
altrimenti. Scrivete un breve programma per verificare che la vostra
soluzione è corretta.Obiettivo: Familiarizzare con le convenzioni per gli identificatori e con i commenti.
Attività:
/* ... */
o // ...
).Obiettivo: Affrontare alcuni problemi e trucchi ricorrenti nella programmazione.
Attività:
x
può assumere i
valori 0 o 1 (e nessun altro). Scrivete, usando l'operatore
condizionale "?:
", un'istruzione di assegnamento che
assegna alla variabile y
il valore 1 se x
è 0 e il valore 0 se x
è 1. Scrivete un
breve programma per verificare che la vostra soluzione è
corretta. Nota Anche se questa soluzione funziona,
non è sicuramente la migliore possibile: si vedano i punti
seguenti.x
può assumere i
valori 0 o 1 (e nessun altro). Usate, invece dell'operatore
condizionale "?:
", l'operatore di modulo "%
"
e scrivete un'istruzione di assegnamento che assegna alla variabile
y
il valore 1 se x
è 0 e il valore 0
se x
è 1. Scrivete un breve programma per
verificare che la vostra soluzione è
corretta. Suggerimento Se a y
assegnate
x + 1
, il risultato è corretto se x
vale 0, ma se x
vale 1 in y
vi ritrovate il
valore 2. Però se prendete il "modulo 2"...
Nota Anche questa soluzione, pur funzionante, non
è la migliore possibile: si veda il punto seguente.?:
" e modulo "%
" non sono
necessari. Anzi, sono di gran lunga superflui e si può fare di
meglio senza. Come? Scrivete un breve programma per verificare che la
vostra soluzione è corretta. Suggerimento:
chiedetevi quanto vale x + y
, e risolvete
l'equazione... ?:
".turno
, di tipo int
. Supponiamo per
semplicità che ci siano 2 giocatori; quindi turno
dovrà assumere in sequenza i valori 0, 1, 0, 1, 0,
1,... Scrivete l'istruzione di assegnamento che dal valore corrente di
turno
calcola il valore
successivo. Suggerimento Non vi sarete già
dimenticati le prime tre attività di questo esercizio, no?
;-)turno
dovrà assumere in sequenza
i valori 0, 1, 2, 3, 0, 1, 2, 3, 0, ... e così via. Completate
il programma seguente, scrivendo l'istruzione di assegnamento che
assegna di volta in volta a turno
il valore corretto
sulla base del valore precedente. Suggerimento:
Quali possono essere i valori assunti dal resto della divisione di un
numero per 4?
class Turno { public static void main(String[] args) { int turno; int i; turno = 0; i = 0; while (i <= 20) { System.out.println("Giro numero " + i + " Ora e' di turno giocatore" + turno); turno = //COMPLETARE!! i = i + 1; } } }
turno
assuma ciclicamente i valori 0, 1, 2, 3, 4, 5 e 6?x
il valore di y/2
se y
è pari, e il valore di y/3
se y
è dispari. Usate l'operatore condizionale
"?:
". Scrivete un programma per verificare la
correttezza.x
il valore di y/2
se y
è pari, e il valore di (y-1)/2
se y
è dispari. Scrivete un programma per verificare la
correttezza. Suggerimento: NON usate l'operatore
condizionale.<<
")
equivale ad effettuare una moltiplicazione per 2.Obiettivo: Approfondimenti su alcuni problemi e trucchi ricorrenti nella programmazione.
Attività:
Riprendiamo l'esercizio sulla variabile turno
e
vediamo un po' di variazioni sul tema... Mi raccomando: provate TUTTE
quelle che vi sembrano soluzioni scrivendo, compilando ed eseguendo un
programma. Spesso quello che sulla carta sembra corretto, poi non lo
è...
turno
assuma ciclicamente i valori 1, 2, 3 e 4?
Suggerimenti: Ripartite dalla versione originale e
provate a invertire l'ordine delle operazioni nell'istruzione di
assegnamento. Una volta che ottenete 0, 1, 2, 3, 4, 1, 2, 3, 4, 1,
2,... ci siete quasi, basta cambiare il valore iniziale di
turno
...turno
assuma ciclicamente i valori
0, 2, 4 e 6? Suggerimento: Beh, è molto simile
al problema iniziale: invece di incrementare turno
turno
di 1 lo incrementerete di ...turno
assuma ciclicamente i valori
1, 3, 5 e 7? Suggerimento: partendo dalla versione
precedente, e con una modifica simile a quella fatta in
precedenza...turno
assuma ciclicamente i valori
2, 4, 8 e 16?turno
assuma ciclicamente i valori
2, 4, 16 e 256? Suggerimento: è più
semplice se invertite l'ordine delle istruzioni di assegnamento a
turno
e di visualizzazione
(System.out.println
).Obiettivo: Comprendere i concetti delle promozioni (conversioni automatiche di tipo) e del cast.
Attività: In alcuni casi il Java effettua una conversione automatica di tipo (detta promozione): ad esempio, nell'ultima riga del seguente frammento di codice
byte b; int i; b = 10; i = b;
il valore di b
(che è di tipo byte
)
viene promosso al tipo int
per poter essere assegnato a
i
. Infatti se scrivete un semplice programma contenente
il frammento precedente, esso viene compilato ed eseguito senza
errori. Fatelo...
Le promozioni vengono effettuate automaticamente per le conversioni
byte -> short -> int -> long -> float -> double
, come si
può dedurre dal programma seguente (comprendetelo, compilatelo
ed eseguitelo):
class Prova { public static void main (String[] args) { byte b; short s; int i; long l; float f; double d; b = 10; s = b; i = s; l = i; f = l; d = f; } }
Conversioni automatiche di questo tipo sono comode, perché
sicuramente non portano a errori (se un valore ci sta in un
byte
, ci sta senz'altro anche in uno short
e
così via).
La conversione nel verso opposto può però essere pericolosa. Se proviamo a compilare un programma contenente questo frammento di codice
byte b; int i; i = 128; b = i;
il compilatore segnala un errore di "tipo incompatibile"
sull'assegnamento b = i
(verificatelo! E, da qui in poi,
prestate particolare attenzione ai messaggi di errore in
compilazione e in esecuzione). E ciò è sensato, in
quanto 128 non ci sta in un byte
(che, ricordo, usa la
rappresentazione in complemento a due). Questo comportamento
può però sembrare scomodo nel caso in cui il valore di
i
sia sufficientemente piccolo da stare in un byte, come
in
byte b; int i; i = 10; b = i;
Anche qui il nostro povero compilatore segnala lo stesso errore
(verificatelo). Mettiamoci nei suoi panni. Io, povero
compilatore, mi baso sui tipi di i
e b
: se
essi sono tali che i
può contenere valori che non
stanno in b
, io devo segnalare errore. E questo è
ragionevole: se i
e b
fossero dati di input
del programma, come potrei sapere quale valore assumeranno
b
e i
?
Per risolvere questi problemi in Java è stato introdotto il
meccanismo del cast (che in inglese significa "fondere in uno
stampo") che permette di convertire un tipo in un altro: è
sufficiente indicare fra parentesi tonde il tipo che si vuole ottenere
(vi sono operatori di cast per tutti i tipi: (byte)
,
(short)
, (int)
, ecc.). In questo modo, il
programmatore deve segnalare esplicitamente nel codice che "sa quello
che fa" e che se ne assume tutte le responsabilità. Il
frammento di codice precedente andrebbe riscritto come:
byte b; int i; i = 10; b = (byte)i;
dove (byte)
è l'operatore di cast (verificate
compilando ed eseguendo). In questo modo il valore di i
(che è un int
) viene convertito in un
byte
; se il valore ci sta in un byte, nessun problema; se
il valore è troppo grande invece qualche problema c'è.
Infatti, un programmatore distratto potrebbe anche scrivere
byte b; int i; i = 128; b = (byte)i;
ritrovandosi in b
lo "strano" valore -128 (verificatelo,
e cercate di capire perché!), con somma soddisfazione del
compilatore...
Obiettivo: Comprendere le problematiche dei valori temporanei delle espressioni.
Attività: Riflettete sul programma seguente:
class Prova { public static void main (String[] args) { int m; int n; int i; m = 2000000000; n = 2000000000; i = m * n / m; System.out.println(i); } }
Quale risultato vi aspettate? Rispondete prima di provare ad eseguirlo, poi provate ad eseguirlo e cercate di capire perché non è come pensate...
La spiegazione è che i valori temporanei nelle
espressioni sono memorizzati in variabili implicite di tipo
int
, a meno che nell'espressione non compaiano variabili
di tipo più ampio (long, float, double
), nel qual
caso vengono usate variabili implicite di quel tipo. Quindi, il
programma precedente viene compilato senza errori, l'esecuzione
avviene anch'essa senza errori, ma il valore stampato è 0
(zero). Questo accade perché il risultato di m * n
viene memorizzato in una variabile implicita di tipo int
,
che non è in grado di contenerlo (e non viene segnalato
l'overflow).
Il programma andrebbe riscritto dichiarando le tre variabili di
tipo long
(in questo modo la variabile temporanea
dell'espressione i = m * n / m
è un
long
), oppure dichiarando solo i
di tipo
long
e utilizzando il cast i = (long)m * n /
m
.
Come consigli generali, ricordate che uno spreco di qualche byte di
solito non è importante e quindi conviene usare variabili dei
tipi più ampi (long
e double
);
inoltre bisogna cercare di evitare i cast che portano spesso a
errori. Riparleremo in modo più generale del cast fra qualche
lezione...
... RICORDATEVI DI USCIRE (exit + LOGOUT) E DI NON SPEGNERE IL CALCOLATORE!!!!!