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.y
deve assumere un valore dato da
"se x
vale zero, allora 1, altrimenti 0 (ossia se
x
vale uno, allora 0)". In Java, usando l'operatore
condizionale, si ha quindi:
y = (x == 0) ? 1 : 0;(le parentesi non sono necessarie ma aiutano la comprensione). Un programma che verifica la correttezza della nostra risposta può essere:
class Trucchi { public static void main(String[] args) { int x, y; x = 0; y = x == 0 ? 1 : 0; System.out.println(y); x = 1; y = (x == 0) ? 1 : 0; System.out.println(y); } }(a
x
viene assegnato prima 0
e poi
1
; in entrambi i casi l'espressione viene valutata e il
risultato visualizzato). L'esecuzione del programma è:
>javac Trucchi.java >java Trucchi 1 0Ripetiamo ancora una volta che questa non è una buona soluzione, e passiamo subito a vederne di migliori.
%
" (e viene usato come in
13 % 5
).
y
il modulo 2 dell'espressione
x + 1
", ossia, in Java
y = (x + 1) % 2;Vediamo di capire perché funziona. Possiamo vederlo in due modi:
y
il valore successivo al
valore di x
, ma "rimanendo confinati" (grazie al modulo
2) fra 0
e 1
, che sono gli unici valori che
può assumere il resto della divisione per due. Quindi, il
"valore successivo" di 0
è 1
e il
"valore successivo" di 1
è 0
(perché due "uscirebbe dai confini" e quindi "si ricomincia
daccapo").x
vale
0
, allora l'espressione alla destra dell'assegnamento
vale (0 + 1) % 2
, ossia
1 % 2
, ossia 1
(uno diviso due fa
zero con resto uno); se x
vale 1
, allora
l'espressione alla destra dell'assegnamento vale
(1 + 1) % 2
, ossia
2 % 2
, ossia 0
(due diviso due fa
uno con resto zero).x
vale 0
, y
vale
1
e se x
vale 1
, y
vale 0
. Quindi, x + y
può
valere solo 1
. Ma allora abbiamo l'equazione
x + y = 1
, che possiamo
riscrivere come y = 1 - x
. E
questa equazione può essere immediatamente trasformata
nell'istruzione
y = 1 - x;Osservate che il simbolo "
=
" nelle righe precedenti ha
due significati: in termini matematici indica l'uguaglianza, mentre
nel codice Java significa piuttosto "diventa" (ad esempio, l'ultima
istruzione può essere letta come "y
diventa
1
meno x
"). Il breve programma di verifica
è sempre analogo ai precedenti. ?:
" e "%
").x
è:
x
se x
è positivo;-x
se x
è negativo.y
il valore assoluto
di x
:
y = x >= 0 ? x : -x;Altre soluzioni sono:
y = x > 0 ? x : -x;
y = x < 0 ? -x : x;
y = x <= 0 ? -x : x;
y = 1 - x;dell'attività 3 e osservare che qui siamo in una situazione analoga, con l'unica differenza che c'è un'unica variabile (
turno
) anziché due (x
e
y
), e che turno
svolge il ruolo di entrambe:
va usato nella parte destra dell'assegnamento per calcolare il nuovo
valore, e va usato nella parte sinistra dell'assegnamento per assumere
il nuovo valore. Quindi la risposta (migliore) è:
turno = 1 - turno;L'uso di una ulteriore variabile, come in
int temp; temp = 1 - turno; turno = temp;è ovviamente superfluo e porta a una soluzione meno efficiente ed elegante. Infatti, non ha nessun senso fare un passo "intermedio" che in realtà non è intermedio per niente... (è solo in più!)
turno = turno == 0 ? 1 : 0;o
turno = (turno + 1) % 2;Cosa dire, se non che siete "caldamente" invitati a rivedervi con più attenzione le attività precedenti??
turno
fra 0
e 3
. L'istruzione
completa da inserire nel programma è:
turno = (turno + 1) % 4;Vediamo perchè funziona. Questo assegnamento fa sì che:
turno
vale 0
, esso diventa
1
(0 + 1
);turno
vale 1
, esso diventa
2
(1 + 1
);turno
vale 2
, esso diventa
3
(2 + 1
);turno
vale 3
, esso diventa
0
(3 + 1 % 4
vale
4 % 4
ossia 0
).>java Turno Giro numero 0 Ora e' di turno giocatore 0 Giro numero 1 Ora e' di turno giocatore 1 Giro numero 2 Ora e' di turno giocatore 2 Giro numero 3 Ora e' di turno giocatore 3 Giro numero 4 Ora e' di turno giocatore 0 Giro numero 5 Ora e' di turno giocatore 1 Giro numero 6 Ora e' di turno giocatore 2 Giro numero 7 Ora e' di turno giocatore 3 Giro numero 8 Ora e' di turno giocatore 0 Giro numero 9 Ora e' di turno giocatore 1 Giro numero 10 Ora e' di turno giocatore 2 Giro numero 11 Ora e' di turno giocatore 3 Giro numero 12 Ora e' di turno giocatore 0 Giro numero 13 Ora e' di turno giocatore 1 Giro numero 14 Ora e' di turno giocatore 2 Giro numero 15 Ora e' di turno giocatore 3 Giro numero 16 Ora e' di turno giocatore 0 Giro numero 17 Ora e' di turno giocatore 1 Giro numero 18 Ora e' di turno giocatore 2 Giro numero 19 Ora e' di turno giocatore 3 Giro numero 20 Ora e' di turno giocatore 0Osserviamo che si sarebbe potuto scrivere un programma con comportamento analogo sfruttando la variabile
i
, ma
così facendo non avremmo risolto l'esercizio, che ci chiedeva
esplicitamente di scrivere "l'istruzione di assegnamento che assegna
di volta in volta a turno
il valore corretto sulla
base del valore precedente."
turno = (turno + 1) % 7;Verificatene da soli il funzionamento.
x
un valore ottenuto in due modi
diversi a seconda se y
sia pari o dispari. Come visto
nell'attività 2 dell'esercizio 6, per sapere se y
è pari basta verificare il valore dell'espressione booleana
y % 2 == 0
. Quindi:
x = y % 2 == 0 ? y / 2 : y / 3;Notate il modo di ragionare. Non stiamo dicendo "se
y
è pari assegno a x
il valore
y / 2
e se y
è dispari
assegno a x
il valore
y / 3
". Stiamo invece dicendo: "devo assegnare
a x
un valore; se y
è pari il valore
è y / 2
e se y
è
dispari il valore è
y / 3
". L'assegnamento viene fatto "a monte".
x = y % 2 == 0 ? y / 2 : (y - 1) / 2;(le parentesi sono necessarie perché l'operatore di divisione è prioritario rispetto a quello di sottrazione). Questa risposta è corretta, ma non è la migliore. Il suggerimento dovrebbe mettervi la pulce nell'orecchio... Come funziona la divisione intera? Se il dividendo (il numero che viene diviso) è pari, non succede niente di strano; se il dividendo invece è dispari, il risultato è lo stesso che si otterrebbe sottraendo uno al dividendo (ad esempio, tredici diviso due fa sei, così come dodici...). Ma allora lo "scrupolo" di sottrarre
1
a y
quando y
è dispari
è uno scrupolo completamente inutile, tanto ci pensa già
la divisione intera... Quindi la risposta corretta è
semplicemente:
x = y / 2;Questo assegnamento darà il risultato voluto in entrambi i casi.
class ScorrimentoVersoSinistra { public static void main(String[] args) { int x; x = 3; System.out.println(x << 1); } }