Capitolo 4

Condizioni: far scegliere al programma

Fino ad ora i programmi eseguivano tutte le istruzioni in ordine. Adesso impari a decidere: eseguire un pezzo di codice solo se una condizione è vera. Si impara in due modi: if (se) e when (quando).

In questo capitolo impari

Ripasso lampo: leggere i dati e convertirli

Prima di decidere, il programma deve avere i dati: ripasso del capitolo 3. Negli esempi di questo capitolo le letture usano readlnOrNull()?.toIntOrNull() ?: 0: se il dato manca o non è un numero, la condizione riceve comunque 0.

L'utente scrive 12. Che cosa stampa questo programma?

fun main() {
    val n = readln()
    println(n + 1)
}
13No: n è testo, quindi n + 1 attacca i due pezzi invece di sommarli.
121Esatto: manca toInt(). Con readln().toInt() il risultato sarebbe 13.
Un erroreKotlin non dà errore: trasforma l'1 in testo e unisce tutto in una stringa.

readln() restituisce sempre testo: senza .toInt() il + unisce invece di sommare. È l'errore più frequente di tutto il manuale.

L'utente scrive ciao. Che cosa stampa questo programma?

fun main() {
    val n = readln().toIntOrNull() ?: 0
    println(n * 2)
}
0Esatto: toIntOrNull() dà null, e ?: mette 0 al suo posto. 0 * 2 è 0.
Un errore di conversioneQuesto succederebbe con toInt(); con toIntOrNull() il programma continua.
ciaociaoNo: ?: scarta il testo non convertibile e usa il valore di riserva.

toIntOrNull() non fa mai fermare il programma: quando la conversione non riesce restituisce null, e il valore di riserva lo scegli tu con ?:.

4.1 if: eseguire solo se...

La forma base è if (condizione) { ... }: le istruzioni fra graffe vengono eseguite solo se la condizione è vera. Con else si indica che cosa fare quando è falsa.

Le graffe si possono evitare, ma...

Se il ramo contiene una sola istruzione puoi scrivere if (eta >= 18) println("Maggiorenne"). Per cominciare usa sempre le graffe: il codice resta chiaro e non fai errori quando aggiungi istruzioni.

4.2 Confronti e operatori logici

Una condizione è un'espressione che vale true o false. Si costruisce con gli operatori di confronto e si combina con quelli logici.

OperatoreSignificatoEsempio
==uguale a (due segni di uguale!)voto == 10
!=diverso danome != "Anna"
> >=maggiore, maggiore o ugualeeta >= 18
< <=minore, minore o ugualeprezzo < 10
&&e: vero solo se lo sono entrambeeta >= 14 && eta < 18
||o: vera se lo è almeno unagiorno == "sab" || giorno == "dom"
!non: rovescia vero e falso!haLaSpada

Che cosa stampa questo programma?

fun main() {
    val eta = 16
    if (eta >= 14 && eta < 18) println("motorino")
    else println("no")
}
noNo: 16 >= 14 è vera e 16 < 18 è vera, quindi si entra nel primo ramo.
motorinoEsatto: con && servono entrambe vere. Qui lo sono.
Non stampa niente, perché manca un terzo ramoNo: c'è l'else, e comunque la prima condizione è già vera.

&& è vero solo se lo sono tutte e due. Basta una falsa e si va nell'else.

Un solo uguale assegna, due uguali confrontano

if (eta = 18) è un errore: con = si assegna un valore. Per confrontare si usa sempre ==.

4.3 else if: più di due possibilità

Quando le possibilità sono più di due si usano uno o più else if, ma si chiude sempre con else. Kotlin le controlla dall'alto verso il basso e si ferma alla prima condizione vera.

Nel prossimo riquadro c'è anche un if che non stampa: mette un testo in una variabile. Lo spieghiamo nella sezione dopo. Per ora concentra sull'else if: quale ramo parte con voto = 7?

  1. Con voto = 7 la prima condizione (7 >= 9) è falsa, la seconda (7 >= 6) è vera: Kotlin stampa Promosso e non guarda le condizioni successive.
  2. Il secondo if non stampa niente: restituisce il testo "promosso/a", che finisce nella variabile esito. È l'if che dà un valore, spiegato nella sezione 4.4.
  3. Da qui le due righe di output: Promosso e Esito: promosso/a.
Perché l'ordine conta

Con voto = 9 la prima condizione è vera e il programma non guarda le altre. Se avessi messo per prima voto >= 6, anche un 10 sarebbe finito in "Sufficiente": si va sempre dal caso più restrittivo a quello più generico.

Che cosa stampa questo programma?

fun main() {
    val voto = 9
    if (voto >= 6) println("sufficiente")
    else if (voto >= 9) println("ottimo")
    else println("insufficiente")
}
ottimoIl voto 9 soddisfa anche 9 >= 6, e quella condizione viene controllata prima.
sufficienteEsatto: Kotlin si ferma alla prima condizione vera, quindi il ramo >= 9 non viene mai raggiunto con questo ordine.
insufficienteNo: la prima condizione è vera, quindi si entra in quel ramo.

Le condizioni vanno scritte dalla più stretta alla più larga. Qui l'ordine giusto è >= 9 prima e >= 6 dopo.

4.4 if come espressione: assegnare un valore

In Kotlin if può restituire un valore: è il modo più compatto per scegliere fra due valori e metterli in una variabile.

4.5 when: tanti casi in modo ordinato

when confronta un valore con tanti casi. È come una catena di else if, ma molto più leggibile. Il caso else è obbligatorio quando nessuna condizione copre tutti i valori possibili.

Che cosa significa in 0..59

in 0..59 si legge "da 0 a 59, estremi compresi". Il when sceglie quel ramo se il voto è dentro quell'intervallo. Con il voto 78: 0–59 no, 60–100 sì → stampa Sufficiente. Gli intervalli 1..10 li userai anche nei cicli, nel capitolo 5. L'idea è la stessa: un tratto di numeri.

  1. 6 non è nella prima riga di casi, ma è nella seconda: Weekend.
  2. 15 > 10 è vero e 15 % 5 == 0 è vero: con && servono entrambe vere, quindi true.
  3. 15 > 20 è falso e 15 < 0 è falso: con || basta una condizione vera, quindi false.

Che cosa stampa questo programma?

fun main() {
    val n = 15
    when {
        n % 15 == 0 -> println("3 e 5")
        n % 3 == 0 -> println("solo 3")
        else -> println("altro")
    }
}
solo 315 è divisibile anche per 3, ma quella riga sta dopo. Kotlin si ferma alla prima vera.
3 e 5Esatto: 15 % 15 == 0 è la prima condizione vera.
altroNo: la prima condizione è vera, l'else non si raggiunge.

Anche nel when vale l'ordine: si prende il primo caso vero e gli altri non si guardano.

when anche come espressione

val premio = when (punti) { ... else -> 0 } funziona come if: il valore del ramo scelto diventa il valore di tutta l'espressione.

Due usi del when, una regola semplice

1. Il when esegue istruzioni (esempi 9–11). Se nessun caso coincide, non succede niente. Nessun errore.

2. Il when dà un valore (val premio = when (punti) { ... }). Allora l'else è obbligatorio: Kotlin deve sapere che cosa mettere nella variabile in ogni caso.

Nel laboratorio qui sotto l'else c'è già e il programma funziona. Prova tu: commenta la riga else -> "niente", esegui, leggi l'errore. Poi togli il commento e riesegui: deve stampare Premio: niente. Abitudine: metti sempre l'else.

4.6 Riepilogo

ScriviSignificato
if (c) { ... } else { ... }due strade possibili
if (c) { ... } else if (c2) { ... } else { ... }più strade, in ordine
val x = if (c) a else buna variabile che prende uno dei due valori
when (x) { 1 -> ...; else -> ... }confronto con tanti casi
when { c1 -> ...; c2 -> ...; else -> ... }catena di condizioni

4.7 Esercizi guidati

Leggi un voto da 0 a 10. Attenzione: le fasce non sono quelle dell'esempio 5. Qui usi questa scala, da 0 a 10: 10 → Eccellente; 8 o 9 → Buono; 6 o 7 → Sufficiente; sotto 6 → Insufficiente. Usa if / else if / else. Parti dal caso più alto. Stampa una sola parola: con il voto 5 deve uscire Insufficiente.

Parti dal caso più alto o dal più basso e prosegui in ordine: if (voto >= 10) ... else if (voto >= 8) ....

  1. Le condizioni vanno dal caso più alto al più basso. Con voto = 10 la prima condizione è vera e le altre non vengono nemmeno guardate.
  2. Se l'input non è un numero, ?: -1 assegna -1 e il programma finisce nel ramo else invece di fermarsi con un errore.
  3. Ogni ramo ha un solo println: avresti potuto omettere le graffe, ma così il codice è pronto per aggiungere altre istruzioni.

Leggi un anno e stabilisci se è bisestile. Regola: un anno è bisestile se è divisibile per 4, tranne gli anni divisibili per 100, che sono bisestili solo se divisibili per 400. Prova con 1900 (non bisestile), 2000 (bisestile) e 2024 (bisestile). Con 1900 stampa esattamente 1900 non è un anno bisestile.

Puoi scrivere tutte le condizioni in un unico if con && e ||: anno % 4 == 0 && (anno % 100 != 0 || anno % 400 == 0).

  1. anno % 4 == 0 è la condizione principale: senza quella nessun anno può essere bisestile.
  2. Le parentesi attorno a (anno % 100 != 0 || anno % 400 == 0) sono essenziali: && ha la precedenza su ||, quindi senza parentesi il significato cambierebbe.
  3. 1900 è divisibile per 4 e per 100 ma non per 400 → non bisestile. 2000 è divisibile per 400 → bisestile. 2024 non è divisibile per 100 → bisestile.
  4. Il risultato viene messo in una val con un nome chiaro: il codice che segue si legge come una frase in italiano.

Il distributore automatico ha questo menu: 1 = acqua (0.50), 2 = succo (1.20), 3 = caffè (0.90), 4 = snack (1.50). Leggi il numero scelto e i soldi inseriti, poi stampa cosa è stato scelto (Hai scelto: ...) e una seconda riga: il resto, oppure Soldi insufficienti: mancano ... euro. Se il numero non è nel menu: Hai scelto: Scelta non valida e Nessun prodotto erogato. Con scelta 3 e 1.50 euro: Hai scelto: Caffè e Resto: 0.6 euro.

Con when assegna il prezzo: val prezzo = when (scelta) { 1 -> 0.50 ... else -> 0.0 }. Il resto è soldi - prezzo: se il prezzo è 0.0 non c'è nulla da comprare.

  1. Un when come espressione restituisce il valore del ramo scelto: il primo lo usiamo per il nome, il secondo per il prezzo.
  2. L'else del when serve sia per il nome sia per il prezzo: se la scelta non è 1-4 il prezzo resta 0.0.
  3. Dopo il when ci sono tre casi distinti gestiti con if / else if / else: prezzo nullo, soldi sufficienti, soldi insufficienti.
  4. soldi >= prezzo confronta due Double: il confronto funziona anche con i decimali.

Questo programma parte senza errori, ma con voto = 9 stampa Sufficiente invece di Ottimo. Eseguilo, confronta l'output con quello atteso e correggi solo l'ordine delle condizioni, senza cambiare i testi stampati.

Kotlin si ferma alla prima condizione vera: il caso più stretto (voto >= 9) deve stare prima di quello più largo (voto >= 6).

  1. Il codice di partenza compila: l'errore è logico, non di sintassi. Con voto = 9 la prima condizione 9 >= 6 è già vera.
  2. Kotlin entra nel primo ramo e non guarda gli altri: il ramo >= 9 diventa irraggiungibile.
  3. Mettendo voto >= 9 per primo, il 9 finisce in "Ottimo", il 7 in "Sufficiente" e il 4 in "Insufficiente".
  4. Regola generale: le condizioni vanno scritte dalla più stretta alla più larga.

Il programma deve dire se un voto è sufficiente, ma non funziona. Con 7 deve stampare due righe: Sufficiente e Voto valido. Con 5 deve stampare solo Insufficiente.

Per confrontare serve == (due uguali); le due condizioni di un && stanno dentro le stesse parentesi tonde dell'if.

  1. if (voto = 6) usa un solo =, che assegna: per confrontare si scrive voto >= 6 o voto == 6.
  2. Le graffe non sono obbligatorie con una sola istruzione, ma con if e else su più righe evitano di sbagliare a quale ramo appartiene l'istruzione.
  3. if (voto >= 6) && (voto <= 10) chiude le parentesi troppo presto: la condizione completa va dentro una sola coppia di parentesi, if (voto >= 6 && voto <= 10).
  4. Kotlin controlla tutta la condizione e solo dopo decide: un && fuori posto non è "quasi giusto", è un errore.
  • so scrivere una condizione con if, else if e else
  • so scegliere fra >, <, >=, <=, ==, !=
  • so combinare le condizioni con &&, || e !
  • so usare if come espressione per assegnare un valore
  • so scrivere un when con valori, intervalli e casi else
  • so spiegare perché l'ordine delle condizioni cambia il risultato
  • so leggere un messaggio di errore e correggere il programma da solo