Cómo escribir instrucciones switch en Go

Introducción

Las instrucciones condicionales brindan a los programadores la capacidad indicar a sus programas que realicen una acción si una condición es verdadera y otra si la condición es falsa. Con frecuencia, nos conviene comparar alguna variable con múltiples valores posibles y realizar diferentes acciones en cada circunstancia. Es posible lograr esto utilizando solo instrucciones if. Sin embargo, escribir software no solo se trata de lograr que las cosas funcionen, sino también de comunicar su intención a usted mismo en el futuro y a otros desarrolladores. switch es una instrucción condicional alternativa útil para comunicar las acciones realizadas por sus programas de Go cuando se les presentan diferentes opciones.

Todo lo que podamos escribir con la instrucción de switch también se puede escribir con instrucciones if. En este tutorial, veremos algunos ejemplos de lo que puede lograr la instrucción switch, las instrucciones if que sustituye y en que situaciones se aplica de forma más adecuada.

Estructura de las instrucciones Switch

Switch se utiliza comúnmente para describir las acciones efectuadas por un programa cuando a una variable se le asignan valores específicos. En el siguiente ejemplo se muestra la forma en que lograríamos esto usando instrucciones if:

package main  import "fmt"  func main() {     flavors := []string{"chocolate", "vanilla", "strawberry", "banana"}      for _, flav := range flavors {         if flav == "strawberry" {             fmt.Println(flav, "is my favorite!")             continue         }          if flav == "vanilla" {             fmt.Println(flav, "is great!")             continue         }          if flav == "chocolate" {             fmt.Println(flav, "is great!")             continue         }          fmt.Println("I've never tried", flav, "before")     } } 

Esto generará el siguiente resultado:

Outputchocolate is great! vanilla is great! strawberry is my favorite! I've never tried banana before 

Dentro de main, definimos un slice de sabores de helado. Luego usaremos un for loop para iterarlos. Utilizamos tres instrucciones if para imprimir diferentes mensajes que indican preferencias por diferentes sabores de helado. Cada instrucción if debe usar la instrucción continue para detener la ejecución del bucle for, de modo que el mensaje predeterminado al final no se imprima para los sabores de helado preferidos.

Al añadir nuevas preferencias de helado, debemos seguir agregando instrucciones if para manejar los nuevos casos. En los mensajes duplicados, como en el caso de “vanilla” y “chocolate”, deben estar duplicadas las instrucciones if. Para los futuros lectores de nuestro código (incluidos nosotros), la naturaleza repetitiva de las instrucciones if oculta la parte importante de lo que hacen, (cuando se compara la variable con varios valores y tomando y se toman diferentes acciones). Además, nuestro mensaje de reserva se distingue de los condicionales y hace que parezca que no está relacionado. La instrucción switch puede servirnos para organizar mejor esta lógica.

La instrucción switch comienza con la palabra clave switch y le sigue, en su forma más básica, alguna variable contra la cual puedan realizarse comparaciones. A esto le sigue un par llaves ({}) en el que pueden aparecer varias_ cláusulas de caso_. Las cláusulas de caso describen las acciones que su programa de Go debe realizar cuando la variable proporcionada a la instrucción switch es igual al valor referido por las cláusulas de caso. El siguiente ejemplo convierte el ejemplo anterior para que utilice switch en lugar de varias instrucciones if:

package main  import "fmt"  func main() {     flavors := []string{"chocolate", "vanilla", "strawberry", "banana"}      for _, flav := range flavors {         switch flav {         case "strawberry":             fmt.Println(flav, "is my favorite!")         case "vanilla", "chocolate":             fmt.Println(flav, "is great!")         default:             fmt.Println("I've never tried", flav, "before")         }     } } 

El resultado es el mismo que el anterior:

Outputchocolate is great! vanilla is great! strawberry is my favorite! I've never tried banana before 

Una vez más, definimos un segmento de sabores de helado en main y usamos la instrucción range para iterar cada sabor. Sin embargo, esta vez usamos una instrucción switch que examinará la variable flav. Utilizamos dos cláusulas case para indicar las preferencias. Ya no necesitamos las instrucciones continue porque la instrucción switch solo ejecutará una cláusula case. También podemos combinar la lógica duplicada de los condicionales “chocolate” y “vanilla” separando cada uno con una coma en la declaración de la cláusula case. La cláusula default sirve como cláusula general. Se ejecutará para cualquier sabor que no hayamos tenido en cuenta en el cuerpo de la instrucción switch. En este caso, “banana” hará que se ejecute default, con lo cual se imprimirá el mensaje I've never tried banana before.

Esta forma simplificada de las instrucciones switch aborda el uso más común para ellas: comparar una variable con varias alternativas. También nos proporciona practicidad cuando queremos realizar la misma acción para varios valores diferentes y cualquier otra acción cuando no se cumple ninguna de las condiciones enumeradas al usar la palabra clave default.

Cuando esta forma simplificada de switch resulta demasiado limitada, podemos usar una forma más general de la instrucción switch.

Instrucciones generales de switch

Las instrucciones switch son útiles para agrupar las colecciones de condicionales más complicadas a fin de mostrar que están de algún modo relacionadas. Esto se utiliza con mayor frecuencia al comparar una variable con un rango de valores, en lugar de valores específicos como en el ejemplo anterior. El siguiente ejemplo implementa un juego de adivinanzas usando instrucciones if que se podrían beneficiar con una instrucción switch:

package main  import (     "fmt"     "math/rand"     "time" )  func main() {     rand.Seed(time.Now().UnixNano())     target := rand.Intn(100)      for {         var guess int         fmt.Print("Enter a guess: ")         _, err := fmt.Scanf("%d", &guess)         if err != nil {             fmt.Println("Invalid guess: err:", err)             continue         }          if guess > target {             fmt.Println("Too high!")             continue         }          if guess < target {             fmt.Println("Too low!")             continue         }          fmt.Println("You win!")         break     } } 

El resultado puede variar dependiendo del número aleatorio que seleccionó y de lo bien que se desempeñe en el juego. A continuación, se muestra el resultado de una sesión de ejemplo:

OutputEnter a guess: 10 Too low! Enter a guess: 15 Too low! Enter a guess: 18 Too high! Enter a guess: 17 You win! 

En nuestro juego de adivinanzas se requiere un número al azar para compararlas, por lo que usamos la función rand.Intn del paquete math/rand. A fin de asegurarnos de obtener valores diferentes para target cada vez que juguemos, usamos rand.Seed para aleatorizar el generador de números aleatorios según la hora actual. El argumento 100 en rand.Intn nos proporcionará un número dentro del rango de 0 a 100. Luego, usaremos un bucle for para comenzar a recopilar adivinanzas del jugador.

La función fmt.Scanf nos proporciona un medio para leer las entradas del usuario en una variable que elijamos. Se requiere un verbo de cadena de formato que convierta la entrada del usuario al tipo que esperamos. Aquí %d significa que esperamos un int y pasamos la dirección de la variable guess de modo que fmt.Scanf pueda establecer esa variable. Después de manejar cualquier error de análisis, usamos dos instrucciones if para comparar la adivinanza del usuario con el valor target. El string que muestran, junto con bool, controla el mensaje que se presenta al jugador y si el juego se cerrará.

Estas instrucciones if ocultan el hecho de que el rango de valores con los que se compara la variable están todos relacionados de alguna manera. También puede ser difícil, a simple vista, saber si faltó alguna parte del rango. En el siguiente ejemplo se vuelve a refactorizar el ejemplo anterior para usar una instrucción switch en su lugar:

package main  import (     "fmt"     "math/rand" )  func main() {     target := rand.Intn(100)      for {         var guess int         fmt.Print("Enter a guess: ")         _, err := fmt.Scanf("%d", &guess)         if err != nil {             fmt.Println("Invalid guess: err:", err)             continue         }          switch {         case guess > target:             fmt.Println("Too high!")         case guess < target:             fmt.Println("Too low!")         default:             fmt.Println("You win!")             return         }     } } 

Con esto, se mostrará un resultado similar al siguiente:

OutputEnter a guess: 25 Too low! Enter a guess: 28 Too high! Enter a guess: 27 You win! 

En esta versión del juego de adivinanzas, se sustituyó el bloque de instrucciones if por una instrucción switch. Se omite el argumento de la expresión de switch, ya que solo nos interesa recopilar los condicionales juntos utilizando switch. Cada cláusula case contiene una expresión diferente que compara guess con target. Como la primera vez que sustituimos las instrucciones if por switch, ya no necesitamos las instrucciones continue porque solo se ejecutará una cláusula case. Por último, la cláusula default se encarga del caso en el que guess == target, ya que cubrimos todos los demás valores posibles con las otras dos cláusulas case.

En los ejemplos que vimos hasta el momento, se ejecutará exactamente una instrucción caso. De vez en cuando, es posible que desee combinar los comportamientos de varias cláusulas case. Las instrucciones switch proporcionan otra palabra clave para lograr este comportamiento.

Fallthrough

A veces, querrá volver a utilizar el código que otra cláusula case contiene. En estos casos, es posible solicitar que Go ejecute el cuerpo de la siguiente cláusula case enumerada usando la palabra clave fallthrough. En el siguiente ejemplo, se modifica nuestro ejemplo anterior de sabores de helado para reflejar con mayor precisión nuestro entusiasmo por el helado de fresa:

package main  import "fmt"  func main() {     flavors := []string{"chocolate", "vanilla", "strawberry", "banana"}      for _, flav := range flavors {         switch flav {         case "strawberry":             fmt.Println(flav, "is my favorite!")             fallthrough         case "vanilla", "chocolate":             fmt.Println(flav, "is great!")         default:             fmt.Println("I've never tried", flav, "before")         }     } } 

Veremos el siguiente resultado:

Outputchocolate is great! vanilla is great! strawberry is my favorite! strawberry is great! I've never tried banana before 

Como ya lo observamos antes, definimos un segmento string para representar los sabores e iterar este usando un bucle for. La instrucción switch aquí es idéntica a la que vimos antes, pero se agrega la clave fallthrough al final de la cláusula case para “strawberry”. Esto hará que Go ejecute el cuerpo de case “strawberry”:, imprimiendo primero la cadena strawberry is my favorite!. Cuando este encuentre fallthrough, ejecutará el cuerpo de la cláusula case siguiente. Esto hará que el cuerpo de case “vanilla”, “chocolate”: se ejecute e imprima strawberry is great!.

Los desarrolladores de Go no utilizan la palabra clave fallthrough a menudo. Normalmente, la reutilización del código mediante fallthrough se puede obtener de mejor manera definiendo una función con el código común. Por estas razones, generalmente no se recomienda utilizar fallthrough.

Conclusión

Las instrucciones switch nos ayudan a transmitir, a otros desarrolladores que leen nuestro código, que una serie de comparaciones están de alguna manera relacionadas entre sí. Hacen que sea mucho más fácil añadir comportamientos diferentes cuando un nuevo caso se añade en el futuro y permite garantizar que cualquier cosa que se haya olvidado se maneje de manera adecuada también con cláusulas default. La próxima vez que tenga que escribir varias instrucciones if que impliquen todas la misma variable, intente reescribirla con una instrucción switch, le resultará más sencillo volver a trabajar cuando llegue el momento de considerar algún otro valor alternativo.

Si desea obtener más información acerca del lenguaje de programación de Go, consulte toda la serie sobre Cómo realizar codificaciones en Go.