Lección 8 · Control de flujo y funciones
Decisiones con switch
Cuando una decisión tiene muchos caminos, una cadena de else if se vuelve larga y repite lo mismo en
cada línea. Al terminar esta lección vas a poder elegir entre varios caminos con switch: comparar un
valor con una lista de opciones, agrupar varios valores en una opción y escribir una condición en cada
una. También vas a saber leer el error que aparece cuando repites un valor, por qué un break dentro
de un switch no termina el for y cómo salir del for desde un switch.
Elegir según un valor: switch
La caja de la Papelería Central muestra un mensaje distinto según cómo paga cada cliente:
package main
import "fmt"
func main() {
metodo := "tarjeta"
switch metodo {
case "efectivo":
fmt.Println("Paga en la caja")
case "tarjeta":
fmt.Println("Pasa la tarjeta por el lector")
case "transferencia":
fmt.Println("Envía el comprobante")
default:
fmt.Println("Método no aceptado")
}
fmt.Println("¡Gracias por tu compra!")
}Salida:
Pasa la tarjeta por el lector ¡Gracias por tu compra!
switch metodo { toma el valor de metodo y lo compara con el de cada case (“caso”), de arriba
abajo. "efectivo" no coincide; "tarjeta" sí. Go ejecuta las líneas de ese case, y ahí termina el
switch: el programa sigue después de su }, con ¡Gracias por tu compra!.
Cada case lleva un valor, dos puntos (:) y, debajo, las líneas que se ejecutan si coincide. Esas
líneas no van entre llaves: el bloque de un case termina donde empieza el siguiente.
default (“por defecto”) es el bloque que se ejecuta cuando ningún case coincide: con
metodo := "cheque", el programa imprimiría Método no aceptado. Es opcional. Sin default, si nada
coincide, el switch no hace nada. Puede ir en cualquier lugar, pero lo habitual es ponerlo al final.
De todo el switch se ejecuta un solo bloque, o ninguno (salvo con fallthrough, que verás más
adelante en esta lección). Un case también puede quedar sin líneas: si coincide, no hace nada, y el
switch termina igual.
Este switch hace lo mismo que una cadena de if y else if que compara metodo con cada texto,
pero metodo se escribe una sola vez. Como en toda comparación, los valores de los case tienen que
ser del mismo tipo que el del switch: aquí, strings. Con case 1:, el programa no compila.
Si vienes de JavaScript, Python o Java…
En JavaScript, C y el switch clásico de Java (con :), cuando un case coincide, el programa sigue
de largo por los case de abajo hasta encontrar un break. Por eso allá cada case termina con break;. En Go es al revés: cada
case termina solo, y no hace falta break.
Python no tiene switch: lo habitual es if y elif. Desde Python 3.10 existe match, con case
y case _: en el papel de default.
Declarar una variable en el switch
La papelería guarda los cuadernos en cajas de 12. Si el stock no llena la última caja, quiere saber cuántos sobran:
package main
import "fmt"
func main() {
stock := 30
switch resto := stock % 12; resto {
case 0:
fmt.Println("Solo cajas completas")
default:
fmt.Println("Sobran", resto, "cuadernos")
}
}Salida:
Sobran 6 cuadernos
Como en el if, puedes declarar una variable antes del valor, separada con un punto y coma:
switch resto := stock % 12; resto {. Go calcula primero resto, que vale 6, y después lo compara
con cada case. resto existe solo dentro del switch: después de su }, ya no existe.
Varios valores en un case
La papelería abre con horarios distintos según el día. Los días van del 1 (lunes) al 7 (domingo):
package main
import "fmt"
func main() {
dia := 6
switch dia {
case 1, 2, 3, 4, 5:
fmt.Println("Abierto de 8 a 18")
case 6:
fmt.Println("Abierto de 9 a 13")
case 7:
fmt.Println("Cerrado")
default:
fmt.Println("Ese día no existe")
}
}Salida:
Abierto de 9 a 13
Un case puede tener varios valores, separados por comas. Coincide si el valor del switch es igual a
cualquiera de ellos: case 1, 2, 3, 4, 5: es lo mismo que dia == 1 || dia == 2 || …, pero más corto.
dia vale 6, que no está en el primer case y sí en el segundo.
Un valor escrito tal cual puede estar en un solo case. Si el sábado se cuela en la lista de días hábiles, el programa
no compila:
package main
import "fmt"
func main() {
dia := 6
switch dia {
case 1, 2, 3, 4, 5, 6:
fmt.Println("Abierto de 8 a 18")
case 6:
fmt.Println("Abierto de 9 a 13")
}
}Salida:
./main.go:10:7: duplicate case 6 (constant of type int) in expression switch ./main.go:8:22: previous case
Lee el mensaje por partes:
./main.go:10:7apunta al6del segundocase, en la línea 10.duplicate case 6significa “caserepetido:6”.(constant of type int)dice que6es una constante de tipoint: como se compara condia, Go le da el tipo dedia.in expression switchsignifica “en unswitchde expresión”, el nombre que Go le da alswitchcon un valor.- La segunda línea, con sangría, es parte del mismo error:
previous case(“case anterior”) apunta al primer6, al final de la línea 8.
Go ejecuta solo el primer case que coincide, así que el segundo 6 no se usaría nunca. El compilador
no lo deja pasar, porque casi siempre es un valor puesto en el case equivocado. Quita el 6 del
case que no le corresponde: aquí, del primero.
Si vienes de JavaScript, Python o Java…
En JavaScript, Java y C, dos case seguidos, como case 6: case 7:, comparten el bloque de abajo,
porque el primero sigue de largo hasta el segundo. En Go no: un case 6: sin líneas no hace nada, y el
sábado no mostraría ningún horario. Para que dos valores compartan un bloque, ponlos en el mismo case:
case 6, 7:.
Un switch sin valor
A veces, cada opción depende de una condición, no de un valor exacto. Es el estado del stock de la
lección Decisiones con if, escrito con switch:
package main
import "fmt"
func main() {
stock := 3
switch {
case stock == 0:
fmt.Println("Agotado")
case stock < 5:
fmt.Println("Pocas unidades")
default:
fmt.Println("Disponible")
}
}Salida:
Pocas unidades
Aquí no hay nada entre switch y la {. En un switch sin valor, cada case es una condición, como
la de un if: tiene que dar un bool. Go ejecuta el bloque del primer case que da true. Con
stock en 3, stock == 0 da false y stock < 5 da true: sale Pocas unidades.
Es la misma cadena de if, else if y else, sin repetir else if en cada línea. Para Go, un
switch sin valor es un switch true {: busca el primer case igual a true.
Un error típico es dejar el valor después de switch y escribir condiciones en los case:
package main
import "fmt"
func main() {
stock := 3
switch stock {
case stock == 0:
fmt.Println("Agotado")
case stock < 5:
fmt.Println("Pocas unidades")
}
}Salida:
./main.go:8:7: cannot convert stock == 0 (untyped bool value) to type int ./main.go:10:7: cannot convert stock < 5 (untyped bool value) to type int
switch stock { compara stock, un int, con el valor de cada case. Pero stock == 0 es una
condición: da un bool. Para compararlo con stock, Go tendría que convertirlo a int, y no puede.
Lee la primera línea por partes:
./main.go:8:7apunta al primercase, en la línea 8.cannot convert stock == 0significa “no se puede convertirstock == 0”.(untyped bool value)dice qué es: un valorboolsin tipo, el resultado de una comparación.to type intsignifica “al tipoint”, el destock.
La segunda línea es el mismo error en el otro case. Un case 1: en el switch metodo { de la
primera sección da un error parecido: Go tampoco puede convertir el número 1 a string para
compararlo.
Si los case son condiciones, borra el valor: switch {. Si son valores, como case 0:, deja
switch stock {.
El orden de los case
Go revisa los case de arriba abajo y se detiene en el primero que coincide: los de abajo ya no se
revisan. En un switch sin valor, varias condiciones pueden dar true a la vez, y gana la primera:
package main
import "fmt"
func main() {
stock := 0
switch {
case stock < 5:
fmt.Println("Pocas unidades")
case stock == 0:
fmt.Println("Agotado")
}
}Salida:
Pocas unidades
0 también es menor que 5: el primer case ya da true, y stock == 0 nunca se revisa. Este
programa no imprime Agotado con ningún stock. Como en la cadena de else if, pon primero la condición
más estrecha, la que cubre menos casos.
En un switch con valor, esto no pasa con valores escritos tal cual, como 6 o "tarjeta": Go no te
deja repetirlos, así que dos case no pueden coincidir a la vez.
Consejo
Cuando una decisión tiene tres caminos o más, un switch suele leerse mejor que una cadena de
else if: cada opción queda en su línea, alineada con las demás. Para dos caminos, lo habitual es
if y else.
fallthrough: pasar al case siguiente
Los socios de la papelería tienen niveles, y cada nivel suma los beneficios de los de abajo:
package main
import "fmt"
func main() {
nivel := 2
switch nivel {
case 3:
fmt.Println("Envío gratis")
fallthrough
case 2:
fmt.Println("10% de descuento")
fallthrough
case 1:
fmt.Println("Acumula puntos")
}
}Salida:
10% de descuento Acumula puntos
fallthrough (“seguir de largo”) va al final de un case y hace que Go ejecute también las líneas del
case siguiente, sin revisar su valor. nivel vale 2: Go ejecuta ese case, y el
fallthrough lo lleva al case 1.
Tiene que ser la última línea de su case, y no puede ir en el último case del switch, porque no
hay uno siguiente: Go responde cannot fallthrough final case in switch. Casi nunca hace falta: es
bueno reconocerlo si lo ves, pero la mayoría de los switch no lo usan.
break dentro de un switch
La papelería atiende de 12 a 16. A las 14 se corta la luz y tiene que cerrar. Este programa intenta
terminar el for con un break:
package main
import "fmt"
func main() {
for hora := 12; hora <= 16; hora++ {
switch hora {
case 13:
fmt.Println(hora, "- almuerzo")
case 14:
fmt.Println(hora, "- se cortó la luz: cerramos")
break
default:
fmt.Println(hora, "- atendiendo")
}
}
}Salida:
12 - atendiendo 13 - almuerzo 14 - se cortó la luz: cerramos 15 - atendiendo 16 - atendiendo
Después de cerrar, la papelería sigue atendiendo a las 15 y a las 16. ¿Por qué no terminó el for?
En la lección Bucles con for viste que break termina el for que lo encierra. En realidad
termina el for o el switch más cercano que lo encierra. Aquí, el más cercano es el switch: el
break termina el switch, que iba a terminar igual, y el for sigue con la hora siguiente.
Para terminar el for desde un case, ponle un nombre al for y úsalo en el break:
package main
import "fmt"
func main() {
horario:
for hora := 12; hora <= 16; hora++ {
switch hora {
case 13:
fmt.Println(hora, "- almuerzo")
case 14:
fmt.Println(hora, "- se cortó la luz: cerramos")
break horario
default:
fmt.Println(hora, "- atendiendo")
}
}
fmt.Println("Papelería cerrada")
}Salida:
12 - atendiendo 13 - almuerzo 14 - se cortó la luz: cerramos Papelería cerrada
horario:, en la línea de arriba del for, es una etiqueta: un nombre para el for, seguido de
dos puntos. break horario termina el for que lleva ese nombre, aunque esté dentro de un switch.
El programa sigue después de la } del for, con Papelería cerrada.
La etiqueta se escribe como un nombre de variable, y gofmt la deja un tabulador más a la izquierda que
el for. Como una variable, tiene que usarse: una etiqueta sin ningún break que la nombre da
label horario defined and not used.
Hay otras dos formas de salir:
- Una variable de control: un
bool, comoabierto := true, que elcasecambia afalsey que elforrevisa en su condición:hora <= 16 && abierto. Elfortermina al revisar la condición, al principio de la vuelta siguiente. Si después delswitchhubiera más líneas, se ejecutarían igual en esa vuelta, la de las 14. - Dentro de una función, como en los ejercicios,
returntermina la función entera, con elfory elswitchincluidos. Lo verás en la lección Funciones.
continue no tiene este problema: un switch no es un bucle, así que un continue dentro de un
switch salta a la vuelta siguiente del for, como siempre.
En resumen
switch valor {compara el valor con cadacase, de arriba abajo, y ejecuta solo el primero que coincide. No hace faltabreak.defaultse ejecuta si ninguno coincide.- Un
casepuede tener varios valores separados por comas, pero un valor escrito tal cual puede estar en un solocase(duplicate case). switch {, sin valor, lleva una condición en cadacasey ejecuta el primero que datrue: el orden importa.fallthroughpasa alcasesiguiente sin revisarlo.- Dentro de un
switch,breaktermina elswitch, no elforque lo encierra. Para salir delfordesde uncase, ponle una etiqueta (horario:) y escribebreak horario.
Ejercicios
Completa la función precioCopia, que devuelve el precio de una fotocopia según su tipo: 100 si es "negro", 500 si es "color" y 0 si es cualquier otro.
Una prueba oculta llama a tu función; main no se ejecuta.
Para salir del editor con el teclado, pulsa Escape y luego Tab.
package main
import "fmt"
// precioCopia devuelve el precio de una fotocopia: 100 en blanco y
// negro, 500 en color y 0 si el tipo no existe.
func precioCopia(tipo string) int {
precio := 0
switch tipo {
case "negro":
precio = 100
// Escribe aquí el case de las copias en color.
}
return precio
}
func main() {
fmt.Println("Copia en negro:", precioCopia("negro"))
fmt.Println("Copia en color:", precioCopia("color"))
}