Flujo de control con instrucciones switch

Completado

Al igual que otros lenguajes de programación, Go admite instrucciones switch. Las instrucciones switch se usan para evitar encadenar varias instrucciones if. Mediante las instrucciones switch, se evita la dificultad de mantener y leer código que incluye muchas instrucciones if. Estas instrucciones también facilitan la construcción de condiciones complicadas. Eche un vistazo a las instrucciones switch de las secciones siguientes.

Sintaxis básica de switch

Al igual que la instrucción if, la condición switch no necesita paréntesis. En su forma más simple, una instrucción switch tiene el siguiente aspecto:

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")
}

Si ejecuta el código anterior varias veces, verá una salida diferente cada vez. (Sin embargo, si ejecuta el código en el área de juegos de Go, obtendrá siempre el mismo resultado. Esa es una de las limitantes del servicio).

Go compara cada caso de la instrucción switch hasta que encuentra una coincidencia para la condición. Observe, sin embargo, que el código anterior no cubre todos los casos posibles de los valores de la variable num. Si num termina siendo 5, la salida del programa es ok.

También puede ser más específico sobre el caso de uso predeterminado e incluirlo de la siguiente manera:

switch i {
case 0:
    fmt.Print("zero...")
case 1:
    fmt.Print("one...")
case 2:
    fmt.Print("two...")
default:
    fmt.Print("no match...")
}

Observe que, en el caso de default, no escribe una expresión de validación. El valor de la variable i se valida con las instrucciones case y el caso default controla los valores no validados.

Uso de varias expresiones

En ocasiones, más de una expresión coincide con una sola instrucción case. En Go, si quiere que una instrucción case incluya más de una expresión, separe las expresiones mediante comas (,). Esta técnica impide que se duplique el código.

En el ejemplo de código siguiente se muestra cómo incluir varias expresiones.

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)
}

Observe que los valores que se incluyen en las expresiones para la instrucción case corresponden al tipo de datos de la variable que valida la instrucción switch. Si incluye un valor entero como una nueva instrucción case, el programa no se compilará.

Invocar una función

Una instrucción switch también puede invocar una función. Desde esa función, puede escribir instrucciones case para los posibles valores devueltos. Por ejemplo, el código siguiente llama a la función time.Now(). La salida que imprime depende del día de la semana actual.

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())
}

Cuando se llama a una función desde una instrucción switch, puede modificar su lógica sin cambiar la expresión porque siempre se valida lo que devuelve la función.

Además, puede llamar a una función desde una instrucción case. Utilice esta técnica, por ejemplo, para buscar coincidencias con un patrón determinado mediante una expresión regular. Este es un ejemplo:

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")
    }
}

Observe que el bloque switch no tiene ninguna expresión de validación. En la próxima sección, hablaremos sobre ese concepto.

Omisión de una condición

En Go, puede omitir una condición en una instrucción switch igual que hace en una instrucción if. Este patrón es parecido a comparar un valor true como si estuviera obligando a que la instrucción switch se ejecutara todo el tiempo.

Este es un ejemplo de cómo escribir una instrucción switch sin una condición:

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")
    }
}

El programa siempre ejecuta este tipo de instrucción switch porque la condición siempre es true. Un bloque switch condicional puede ser más fácil de mantener que una cadena larga de instrucciones if y else if.

Hacer que la lógica lleve al siguiente caso

En algunos lenguajes de programación, se escribe una palabra clave break al final de cada instrucción case. Sin embargo, en Go, cuando la lógica pertenece a un caso, sale del bloque switch a menos que se detenga explícitamente. Para que la lógica lleve al siguiente caso inmediato, use la palabra clave fallthrough.

Para comprender mejor este patrón, examine el siguiente ejemplo de código.

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)
    }
}

Ejecute el código y analice la salida:

15 is less than 50
15 is greater than 100
15 is less than 200

¿Ve que algo esté mal?

Tenga en cuenta que, como num es 15 (menor que 50), coincide con el primer caso. Sin embargo, num no es mayor que 100. Y, como la primera instrucción case tiene una palabra clave fallthrough, la lógica lleva inmediatamente a la siguiente instrucción case sin validar el caso. Por lo tanto, debe tener cuidado al usar la palabra clave fallthrough. Puede que no desee el comportamiento que crea este código.