Flusso di controllo con istruzioni switch
Analogamente ad altri linguaggi di programmazione, Go supporta le istruzioni switch
. Le istruzioni switch
vengono usate per evitare il concatenamento di più istruzioni if
. Usando le istruzioni switch
si evitano le difficoltà che derivano dalla manutenzione e dalla lettura di codice che include molte istruzioni if
. Con queste istruzioni è anche più semplice costruire condizioni complesse. Le istruzioni switch
verranno presentate nelle sezioni seguenti.
Sintassi di base per switch
Analogamente all'istruzione if
, la condizione switch
non richiede le parentesi. Nella sua forma più semplice, un'istruzione switch
ha un aspetto simile al seguente:
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
sec := time.Now().Unix()
rand.Seed(sec)
i := rand.Int31n(10)
switch i {
case 0:
fmt.Print("zero...")
case 1:
fmt.Print("one...")
case 2:
fmt.Print("two...")
}
fmt.Println("ok")
}
Se il codice precedente viene eseguito più volte, verrà visualizzato un output diverso ogni volta. (Ma se si esegue il codice in Go Playground, si otterrà lo stesso risultato ogni volta. Questa è una delle limitazioni del servizio).
Go confronta ogni caso dell'istruzione switch
fino a quando non viene trovata una corrispondenza per la condizione. Si noti tuttavia che il codice precedente non copre tutti i possibili casi dei valori della variabile num
. Se num
finisce per essere 5
, l'output del programma è ok
.
È anche possibile essere più specifici in merito al caso d'uso predefinito e includerlo come segue:
switch i {
case 0:
fmt.Print("zero...")
case 1:
fmt.Print("one...")
case 2:
fmt.Print("two...")
default:
fmt.Print("no match...")
}
Si noti che, nel caso default
non si scrive un'espressione di convalida. Il valore della variabile i
viene convalidato rispetto alle istruzioni case
e l'istruzione case default
gestisce tutti i valori non convalidati.
Usare più espressioni
Occasionalmente, più di un'espressione corrisponde a una sola istruzione case
. In Go, se si vuole che un'istruzione case
includa più di un'espressione, separare le espressioni usando le virgole (,
). Questa tecnica consente di evitare codice duplicato.
L'esempio di codice seguente mostra come includere più espressioni.
package main
import "fmt"
func location(city string) (string, string) {
var region string
var continent string
switch city {
case "Delhi", "Hyderabad", "Mumbai", "Chennai", "Kochi":
region, continent = "India", "Asia"
case "Lafayette", "Louisville", "Boulder":
region, continent = "Colorado", "USA"
case "Irvine", "Los Angeles", "San Diego":
region, continent = "California", "USA"
default:
region, continent = "Unknown", "Unknown"
}
return region, continent
}
func main() {
region, continent := location("Irvine")
fmt.Printf("John works in %s, %s\n", region, continent)
}
Si noti che i valori inclusi nelle espressioni per l'istruzione case
corrispondono al tipo di dati della variabile convalidata dall'istruzione switch
. Se si include un valore intero come nuova istruzione case
, il programma non verrà compilato.
Richiamare una funzione
Con switch
è anche possibile richiamare una funzione. Da questa funzione è possibile scrivere istruzioni case
per i valori restituiti possibili. Ad esempio, il codice seguente chiama la funzione time.Now()
. L'output stampato dipende dal giorno della settimana corrente.
package main
import (
"fmt"
"time"
)
func main() {
switch time.Now().Weekday().String() {
case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
fmt.Println("It's time to learn some Go.")
default:
fmt.Println("It's the weekend, time to rest!")
}
fmt.Println(time.Now().Weekday().String())
}
Quando si chiama una funzione da un'istruzione switch
, è possibile modificarne la logica senza modificare l'espressione perché viene sempre convalidato il risultato restituito dalla funzione.
È anche possibile chiamare una funzione da un'istruzione case
. Usare questa tecnica, ad esempio, per trovare la corrispondenza con un modello specifico usando un'espressione regolare. Ecco un esempio:
package main
import "fmt"
import "regexp"
func main() {
var email = regexp.MustCompile(`^[^@]+@[^@.]+\.[^@.]+`)
var phone = regexp.MustCompile(`^[(]?[0-9][0-9][0-9][). \-]*[0-9][0-9][0-9][.\-]?[0-9][0-9][0-9][0-9]`)
contact := "foo@bar.com"
switch {
case email.MatchString(contact):
fmt.Println(contact, "is an email")
case phone.MatchString(contact):
fmt.Println(contact, "is a phone number")
default:
fmt.Println(contact, "is not recognized")
}
}
Si noti che il blocco switch
non include un'espressione di convalida. Questo concetto verrà approfondito nella sezione successiva.
Omettere una condizione
In Go è possibile omettere una condizione in un'istruzione switch
come in un'istruzione if
. Questo modello equivale a confrontare un valore true
come se si forzasse l'esecuzione dell'istruzione switch
all'infinito.
Di seguito è riportato un esempio di come scrivere un'istruzione switch
senza una condizione:
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().Unix())
r := rand.Float64()
switch {
case r > 0.1:
fmt.Println("Common case, 90% of the time")
default:
fmt.Println("10% of the time")
}
}
Il programma esegue sempre questo tipo di istruzione switch
perché la condizione è sempre true. Un blocco switch
condizionale può essere più facile da gestire rispetto a una lunga catena di istruzioni if
e else if
.
Flusso della logica nel caso successivo
In alcuni linguaggi di programmazione si scrive una parola chiave break
alla fine di ogni istruzione case
. In Go, invece, quando la logica rientra in un caso, esce dal blocco switch
a meno che non la si interrompa in modo esplicito. Per far passare la logica al caso immediatamente successivo, usare la parola chiave fallthrough
.
Per comprendere meglio questo modello, vedere l'esempio di codice seguente.
package main
import (
"fmt"
)
func main() {
switch num := 15; {
case num < 50:
fmt.Printf("%d is less than 50\n", num)
fallthrough
case num > 100:
fmt.Printf("%d is greater than 100\n", num)
fallthrough
case num < 200:
fmt.Printf("%d is less than 200", num)
}
}
Eseguire il codice e analizzare l'output:
15 is less than 50
15 is greater than 100
15 is less than 200
Si nota qualche errore?
Notare che poiché num
è 15 (minore di 50), corrisponde al primo caso. Ma num
non è maggiore di 100. Poiché la prima istruzione case
contiene la parola chiave fallthrough
, la logica passa immediatamente alla successiva istruzione case
senza convalidare il caso. È quindi necessario prestare attenzione quando si usa la parola chiave fallthrough
. Il comportamento creato da questo codice potrebbe non essere quello desiderato.