Lección 6 · Control de flujo y funciones
Decisiones con if
Hasta ahora, tus programas ejecutaban todas sus líneas, de arriba abajo. Al terminar esta lección vas a
poder hacer que un programa decida: ejecutar unas líneas solo si se cumple una condición, o elegir entre
dos caminos o entre varios. También vas a saber leer el error que aparece cuando usas una variable fuera
del if donde la declaraste.
Comparar valores
La Papelería Central revisa el stock de sus cuadernos:
package main
import "fmt"
func main() {
producto := "cuaderno"
stock := 3
agotado := stock == 0
fmt.Println(stock < 5, stock >= 5)
fmt.Println(agotado, stock != 0)
fmt.Println(producto == "cuaderno", producto == "Cuaderno")
}Salida:
true false false true true false
Cada comparación pregunta algo sobre dos valores, y su resultado es un bool: true si se cumple y
false si no. stock vale 3, así que stock < 5 da true y stock >= 5 da false.
Go tiene seis operadores de comparación:
==: igual a.stock == 0dafalse, porque3no es0.!=: distinto de.stock != 0datrue.<y<=: menor que, y menor o igual que.>y>=: mayor que, y mayor o igual que.
== se escribe con dos signos igual. Con uno solo, =, es la asignación que ya conoces: le da un valor
a una variable, no compara nada.
El resultado de una comparación es un valor como cualquier otro: agotado := stock == 0 guarda false
en una variable bool. Los strings también se comparan con == y !=, letra por letra: "cuaderno"
y "Cuaderno" son distintos, porque el segundo empieza con mayúscula.
Solo se comparan valores del mismo tipo. stock == "3" no compila: "3" es un texto, no un número, y
Go responde con el mismatched types que viste en la lección Conversiones de tipo.
Decidir con if
Cuando quedan pocos cuadernos, la papelería quiere un aviso:
package main
import "fmt"
func main() {
stock := 3
if stock < 5 {
fmt.Println("Quedan pocas unidades: pide más.")
}
fmt.Println("Stock:", stock)
}Salida:
Quedan pocas unidades: pide más. Stock: 3
if (“si”, en inglés) va seguido de una condición y de unas líneas entre llaves. Si la condición da
true, Go ejecuta esas líneas; si da false, se las salta y sigue después de la }. Aquí stock < 5
es true, así que sale el aviso. Con stock := 12, el programa imprimiría solo Stock: 12.
Las llaves { y } encierran un bloque: un grupo de instrucciones que van juntas. Ya conoces uno:
el cuerpo de func main. Por convención, las líneas del bloque llevan un tabulador más de sangría, como
las deja gofmt: así se ve qué está dentro del if.
Tres reglas del if en Go:
- La condición tiene que ser un
bool, como una comparación. - Las llaves son obligatorias, aunque el bloque tenga una sola línea.
- La
{va en la misma línea que elif, como enfunc main() {. En la línea siguiente, no compila.
La condición va sin paréntesis: if stock < 5 {. Con paréntesis también compila, pero gofmt los
quita.
Un número no sirve como condición, ni 0 ni ningún otro:
package main
import "fmt"
func main() {
stock := 3
if stock {
fmt.Println("Hay stock")
}
}Salida:
./main.go:7:5: non-boolean condition in if statement
Lee el mensaje por partes:
./main.go:7:5es el lugar: línea 7, columna 5, donde empieza la condición.non-boolean condition in if statementsignifica “condición no booleana en la instrucciónif”. Un valor booleano es unbool:trueofalse.
stock es un int, y Go no decide por su cuenta si un número es verdadero o falso. Escribe la
pregunta completa: if stock > 0 {.
Si vienes de JavaScript, Python o Java…
En JavaScript y en Python, if (stock) o if stock: funcionan: un 0 o un texto vacío cuentan como
falso. Go no hace esa conversión: la condición es siempre un bool.
En JavaScript y en Java, la condición va entre paréntesis y las llaves son opcionales con una sola
línea. En Go es al revés: los paréntesis sobran y las llaves son obligatorias. Si vienes de Python, las
llaves cumplen el papel de la sangría: marcan qué líneas están dentro del if.
= no es ==
Un error típico es escribir un solo signo igual en la condición:
package main
import "fmt"
func main() {
stock := 0
if stock = 0 {
fmt.Println("Agotado")
}
}Salida:
./main.go:7:11: syntax error: cannot use assignment stock = 0 as value
El mensaje dice “no se puede usar la asignación stock = 0 como valor”. stock = 0 le da a stock el
valor 0: no pregunta nada, así que no da true ni false. Para comparar, escribe ==:
if stock == 0 {.
El otro camino: else
La papelería envía gratis los pedidos de 20000 o más:
package main
import "fmt"
func main() {
total := 15000
if total >= 20000 {
fmt.Println("Envío gratis")
} else {
fmt.Println("Envío: 3500")
}
}Salida:
Envío: 3500
else (“si no”) agrega un segundo bloque, que se ejecuta cuando la condición da false. De los dos
bloques se ejecuta siempre uno, y solo uno. total vale 15000, y 15000 >= 20000 es false: sale
el envío de 3500.
El else va en la misma línea que la } que cierra el if: } else {. En la línea siguiente, el
programa no compila:
package main
import "fmt"
func main() {
total := 15000
if total >= 20000 {
fmt.Println("Envío gratis")
}
else {
fmt.Println("Envío: 3500")
}
}Salida:
./main.go:10:2: syntax error: unexpected keyword else, expected }
./main.go:10:2apunta alelse, en la línea 10.unexpected keyword elsesignifica “la palabraelseno se esperaba aquí”.expected }significa “se esperaba}”.
Cuando una línea termina en }, Go da por terminada la instrucción. Para Go, el if terminó en la
línea 9, y ninguna instrucción empieza con else: en ese lugar esperaba otra instrucción o la } que
cierra main. Sube el else a la línea anterior: } else {.
Un valor por defecto, sin else
Muchas veces no hace falta else. Le das a la variable el valor más común y el if lo cambia solo
cuando corresponde:
package main
import "fmt"
func main() {
total := 25000
envio := 3500
if total >= 20000 {
envio = 0
}
fmt.Println("Envío:", envio)
}Salida:
Envío: 0
envio empieza en 3500. Como total es 25000, el if le asigna 0. Dentro del bloque se usa =,
no :=: la variable ya existe, y el if solo cambia su valor. Al final de la lección vas a ver qué
pasa con :=.
Consejo
Si un valor tiene un caso habitual y una excepción, declara la variable con el valor habitual y cambia
solo la excepción con un if. Te ahorras el else, y el código se lee en el orden en que piensas el
problema.
Varias opciones con else if
El stock de un producto puede estar en tres estados:
package main
import "fmt"
func main() {
stock := 3
if stock == 0 {
fmt.Println("Agotado")
} else if stock < 5 {
fmt.Println("Pocas unidades")
} else {
fmt.Println("Disponible")
}
}Salida:
Pocas unidades
else if agrega otra condición. Go las revisa de arriba abajo, ejecuta el bloque de la primera que
da true y se salta todas las demás. Si ninguna da true, se ejecuta el else final. Puedes poner
tantos else if como necesites, y el else final es opcional.
Con stock en 3, stock == 0 da false y stock < 5 da true: sale Pocas unidades, y el
else ya no se revisa.
Como gana la primera condición que da true, el orden importa:
package main
import "fmt"
func main() {
stock := 0
if stock < 5 {
fmt.Println("Pocas unidades")
} else if stock == 0 {
fmt.Println("Agotado")
}
}Salida:
Pocas unidades
0 también es menor que 5: la primera condición ya da true, y stock == 0 nunca se revisa. Este
programa no imprime Agotado con ningún stock. Pon primero la condición más estrecha, la que cubre
menos casos.
Si vienes de JavaScript, Python o Java…
Python escribe elif. Go no tiene elif ni elseif: son dos palabras, else if, en la misma línea
que la } anterior.
Unir condiciones: &&, || y !
A veces una decisión depende de más de una condición:
package main
import "fmt"
func main() {
stock := 8
pedido := 3
total := 15000
socio := true
pausado := false
if pedido > 0 && pedido <= stock {
fmt.Println("Se puede vender")
}
if total >= 20000 || socio {
fmt.Println("Envío gratis")
}
if !pausado {
fmt.Println("Producto a la venta")
}
}Salida:
Se puede vender Envío gratis Producto a la venta
Son los operadores lógicos, y trabajan con valores bool:
&&(“y”) datruesolo si las dos condiciones dantrue.pedido > 0ypedido <= stockse cumplen las dos: se puede vender.||(“o”) datruesi al menos una datrue.total >= 20000dafalse, perosocioestrue: el envío es gratis.!(“no”) da lo contrario de lo que tiene a su derecha.pausadoesfalse, así que!pausadoestrue.
socio y pausado ya son bool, así que van solos en la condición. socio == true también funciona,
pero no agrega nada.
En una misma condición, Go calcula primero las comparaciones, después && y al final ||, igual que
* va antes que +. Por eso pedido > 0 && pedido <= stock no necesita paréntesis. Y por eso estas
dos condiciones son la misma: envío gratis desde 20000, o desde 10000 para los socios.
total >= 20000 || socio && total >= 10000
total >= 20000 || (socio && total >= 10000)Consejo
Cuando mezclas && y || en una condición, escribe los paréntesis aunque no cambien el resultado, como
en la segunda forma. Quien lee no tiene que recordar qué va primero, y gofmt los respeta.
Si vienes de JavaScript, Python o Java…
Python escribe and, or y not. En Go son &&, || y !: con and, el programa no compila.
Python también acepta 1 <= pedido <= stock. En Go, esa línea no compila: Go calcula 1 <= pedido,
que da un bool, y no puede comparar ese bool con stock. Escribe las dos comparaciones unidas con
&&: 1 <= pedido && pedido <= stock.
Como en JavaScript, Python y Java, Go deja de revisar en cuanto sabe el resultado: si lo de la izquierda
de && da false, lo de la derecha no se revisa.
Declarar una variable en el if
La papelería quiere tener al menos 10 cuadernos. Si hay menos, calcula cuántos pedir:
package main
import "fmt"
func main() {
stock := 4
minimo := 10
if faltan := minimo - stock; faltan > 0 {
fmt.Println("Pide", faltan, "unidades")
} else {
fmt.Println("Stock suficiente")
}
}Salida:
Pide 6 unidades
Antes de la condición, un if puede llevar una declaración corta, separada de la condición con un
punto y coma (;). Go ejecuta primero la declaración, faltan := minimo - stock, que guarda 6, y
después revisa la condición, faltan > 0.
La parte del programa donde existe una variable es su alcance (en inglés, scope). Una variable
declarada en la línea del if existe en su condición y en todos sus bloques, también en los de
else if y else. Después de la } final, ya no existe:
package main
import "fmt"
func main() {
stock := 4
minimo := 10
if faltan := minimo - stock; faltan > 0 {
fmt.Println("Pide", faltan, "unidades")
}
fmt.Println("Faltaban", faltan)
}Salida:
./main.go:11:26: undefined: faltan
Lee el mensaje por partes:
./main.go:11:26es el lugar: la línea 11, después delif, donde se usafaltan.undefined: faltansignifica “faltanno está definida”.
faltan sí está declarada, pero en el if: en la línea 11, fuera de su alcance, Go no la conoce. Si
necesitas la variable después del if, declárala antes, en su propia línea: faltan := minimo - stock,
y debajo, if faltan > 0 {.
Consejo
Usa la declaración en el if cuando la variable solo sirve para esa decisión. Así su alcance es el más
corto posible: el resto de la función no la ve y no puede usarla por error.
Variables dentro de un bloque
La regla del alcance vale para todo bloque: una variable declarada entre las llaves de un if existe
solo hasta su }. Usarla después da el mismo undefined.
Esa regla tiene una trampa. Este programa quiere dejar el envío en 0 para un pedido de 25000, pero
dentro del if usa := en lugar de =:
package main
import "fmt"
func main() {
total := 25000
envio := 3500
if total >= 20000 {
envio := 0
}
fmt.Println("Envío:", envio)
}Salida:
./main.go:9:3: declared and not used: envio
El error está en la línea 9, dentro del if: declared and not used: envio significa “envio está
declarada y no se usa”. Pero envio se usa en la última línea… ¿Qué pasó?
envio := 0 declara una variable nueva: crea otra envio, distinta de la de afuera, que solo
existe hasta la } del if. Esa variable nueva no se usa en ningún lado, y la de
afuera sigue en 3500.
En la lección Variables y valores cero viste que := necesita al menos una
variable nueva: no te deja declarar otra vez una que ya existe. Eso vale dentro de un mismo bloque. Un bloque nuevo, como el de un if, sí puede
declarar una variable con el mismo nombre que una de afuera, y desde ahí hasta su }, ese nombre es el
de la nueva.
La corrección es envio = 0, con =: así el if cambia la variable de afuera. Aquí te avisó el
compilador, pero no siempre pasa: si el bloque usara la envio nueva, por ejemplo para imprimirla, el
programa compilaría y la de afuera seguiría valiendo 3500. Para cambiar una variable desde un if,
usa =.
En los ejercicios: completar una función
En los ejercicios de esta lección vas a completar una función: un bloque de código con nombre que recibe valores y devuelve un resultado. Se ve así:
// etiqueta devuelve "Oferta" si el precio es menor que 1000.
func etiqueta(precio int) string {
texto := "Normal"
// Escribe aquí tu código.
return texto
}La primera línea dice cómo se llama la función (etiqueta), qué recibe entre paréntesis (precio, un
int) y de qué tipo es lo que devuelve (string). Dentro, precio es una variable que ya tiene un
valor: el que se pasa al llamar a la función, como en etiqueta(800). return texto termina la función
y devuelve el valor de texto.
Escribe tu código antes del return, sin cambiar la primera línea. Al pulsar Ejecutar, el sitio no
ejecuta main: corre una prueba oculta que llama a tu función con varios valores y revisa lo que
devuelve.
Si alguno no es el esperado, verás una línea como etiqueta(800) = "Normal"; se esperaba "Oferta":
con 800, tu función devolvió "Normal", y tenía que devolver "Oferta". El check_test.go:12 del
principio de la línea es el lugar de la prueba oculta, no de tu código. Las funciones las veremos en la
lección Funciones.
En resumen
- Las comparaciones
==,!=,<,<=,>y>=dan unbool.&&(“y”),||(“o”) y!(“no”) las combinan. if condición { … }ejecuta su bloque solo si la condición datrue. La condición es siempre unbool, las llaves son obligatorias y la{va en la misma línea que elif.} else {da el otro camino.else ifagrega condiciones, que Go revisa de arriba abajo: se ejecuta solo el bloque de la primera que datrue.- Una declaración corta en el
if, comoif faltan := …; faltan > 0 {, crea una variable que solo existe en eseify en suselse. Usarla después daundefined. - Dentro de un bloque,
=cambia una variable de afuera y:=declara otra nueva, aunque tenga el mismo nombre.
Ejercicios
Completa la función envio, que devuelve el costo de envío de un pedido: 3500, o 0 si el total es 20000 o más.
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"
// envio devuelve el costo de envío de un pedido: 3500, o 0 si el
// total es 20000 o más (envío gratis).
func envio(total int) int {
costo := 3500
// Escribe aquí: si el total es 20000 o más, el envío es gratis.
return costo
}
func main() {
fmt.Println("Envío de un pedido de 8000:", envio(8000))
fmt.Println("Envío de un pedido de 25000:", envio(25000))
}