Lección 7 · Control de flujo y funciones
Bucles con for
Muchas tareas de un programa son repetir lo mismo varias veces: imprimir una etiqueta por caja, sumar
las ventas de cada día, pedir cajas hasta llenar el stock. Al terminar esta lección vas a poder repetir
instrucciones con for, el único bucle de Go, en todas sus formas. También vas a saber qué pasa cuando
un bucle no termina nunca, y qué te muestra el sitio en ese caso.
Cambiar una variable: += y ++
Antes de repetir, una forma corta de cambiar una variable a partir de su propio valor. La Papelería Central lleva su stock de cuadernos:
package main
import "fmt"
func main() {
stock := 20
stock += 12
fmt.Println("Llegó una caja:", stock)
stock -= 5
fmt.Println("Se vendieron 5:", stock)
ventas := 0
ventas++
ventas++
fmt.Println("Ventas del día:", ventas)
}Salida:
Llegó una caja: 32 Se vendieron 5: 27 Ventas del día: 2
stock += 12 es una forma corta de escribir stock = stock + 12: toma el valor de stock, le suma
12 y guarda el resultado otra vez en stock. Por eso pasa de 20 a 32. stock -= 5 resta: queda
en 27.
Hay una forma así para cada operador aritmético que ya conoces: +=, -=, *=, /= y %=. Por
ejemplo, precio *= 2 duplica precio. Con strings, += ya lo viste en
Conversiones de tipo: agrega un texto al final.
Sumar o restar 1 es tan común que tiene su propia forma: ventas++ suma 1 a ventas, y ventas--
le resta 1. ventas empieza en 0 y, después de dos ventas++, vale 2.
Los vas a usar en casi todos los bucles de esta lección: para contar, para sumar y para avanzar de un número al siguiente.
En Go, ventas++ es una instrucción completa: no da un valor. No puedes usarla a la derecha de := ni
dentro de un fmt.Println:
package main
import "fmt"
func main() {
ventas := 3
total := ventas++
fmt.Println("Ventas:", total)
}Salida:
./main.go:7:17: syntax error: unexpected ++ at end of statement
./main.go:7:17apunta al++de la línea 7.unexpected ++ at end of statementsignifica “++inesperado al final de la instrucción”: Go leyótotal := ventascomo una instrucción completa, y el++le sobra.
Escribe ventas++ en su propia línea y, en la siguiente, total := ventas.
Si vienes de JavaScript, Python o Java…
En JavaScript, Java y C, ventas++ y ++ventas dan un valor, así que puedes escribir
total = ventas++. En Go, ventas++ es una instrucción sola, y ++ventas, con el ++ adelante, no
existe.
Python no tiene ++: escribe ventas += 1. En Go, ventas += 1 también funciona, pero lo habitual
es ventas++.
Repetir con for
La papelería imprime una etiqueta para cada caja que llega:
package main
import "fmt"
func main() {
for caja := 1; caja <= 3; caja++ {
fmt.Println("Etiqueta para la caja", caja)
}
fmt.Println("Listo")
}Salida:
Etiqueta para la caja 1 Etiqueta para la caja 2 Etiqueta para la caja 3 Listo
for (“para”, en inglés) repite el bloque que va entre sus llaves. Cada repetición se llama una
vuelta (o una iteración). Su primera línea tiene tres partes, separadas por punto y coma (;):
- El inicio,
caja := 1, se ejecuta una sola vez, antes de la primera vuelta. - La condición,
caja <= 3, se revisa antes de cada vuelta. Si datrue, se ejecuta el bloque; si dafalse, el bucle termina. - El paso,
caja++(la instrucción de la sección anterior), se ejecuta al final de cada vuelta.
Sigue el programa paso a paso. caja empieza en 1: 1 <= 3 da true y se imprime la caja 1.
Después, caja++ la lleva a 2, y así hasta la caja 3. Al final de esa vuelta, caja pasa a 4, y
4 <= 3 da false: el bucle termina y el programa sigue después de la }, con Listo.
El bloque del for sigue las mismas reglas que el del if: las llaves son obligatorias, la { va en
la misma línea que el for, y las partes van sin paréntesis. Con paréntesis, como en otros lenguajes,
el programa no compila:
package main
import "fmt"
func main() {
for (caja := 1; caja <= 3; caja++) {
fmt.Println("Etiqueta para la caja", caja)
}
}Salida:
./main.go:6:12: syntax error: unexpected :=, expected )
unexpected :=, expected ) significa “:= inesperado, se esperaba )”. Go tomó el ( como el
inicio de un valor entre paréntesis, como en (precio + 100) * 2, y ahí no puede ir una declaración.
Quita los paréntesis: for caja := 1; caja <= 3; caja++ {.
Si vienes de JavaScript, Python o Java…
En JavaScript y en Java, las tres partes van entre paréntesis: for (let i = 0; i < 3; i++) o
for (int i = 0; i < 3; i++). En Go van sin paréntesis, y la variable se declara con :=.
En Python, for recorre algo, como for caja in range(1, 4):. Go no tiene for … in: para repetir
una cantidad de veces, usa el for de esta sección o el range de la sección siguiente.
Sumar en un bucle
La papelería aparta dinero para una fotocopiadora: 500 la primera semana, 1000 la segunda, 1500 la tercera, y así. ¿Cuánto junta en 4 semanas?
package main
import "fmt"
func main() {
total := 0
for semana := 1; semana <= 4; semana++ {
total += semana * 500
fmt.Println("Semana", semana, "- ahorro:", total)
}
fmt.Println("Total:", total)
}Salida:
Semana 1 - ahorro: 500 Semana 2 - ahorro: 1500 Semana 3 - ahorro: 3000 Semana 4 - ahorro: 5000 Total: 5000
total empieza en 0, y cada vuelta le suma lo que se aparta esa semana: semana * 500. A una
variable así, que junta un resultado vuelta a vuelta, se le llama acumulador.
Fíjate dónde está declarado cada uno. total se declara antes del for, así que existe después
de la } y se puede imprimir al final. semana, en cambio, se declara en la línea del for: como la
variable de un if, existe solo dentro del bucle. Usarla después de la } da undefined: semana.
Consejo
Para la variable que cuenta las vueltas de un bucle corto, lo habitual en Go es un nombre de una letra,
como i. Si el número significa algo (una caja, una semana, un día), un nombre como semana hace que
el bloque se lea solo.
Repetir una cantidad de veces: range
Desde Go 1.22, un for puede recorrer un número entero con range:
package main
import "fmt"
func main() {
for i := range 3 {
fmt.Println("Vuelta", i)
}
for range 2 {
fmt.Println("¡Gracias por tu compra!")
}
}Salida:
Vuelta 0 Vuelta 1 Vuelta 2 ¡Gracias por tu compra! ¡Gracias por tu compra!
for i := range 3 repite el bloque 3 veces. En cada vuelta, i vale un número: 0, 1 y 2.
Empieza en 0 y termina uno antes del número, igual que for i := 0; i < 3; i++, pero más corto.
Con un 0 o un número negativo, no da ninguna vuelta.
Si no necesitas el número de la vuelta, no declares la variable: for range 2 repite el bloque 2
veces y nada más. En el bloque 3 vas a ver que range también recorre listas de valores.
Declarar i y no usarla es el error de siempre, el de toda variable que no se usa:
package main
import "fmt"
func main() {
for i := range 2 {
fmt.Println("¡Gracias por tu compra!")
}
}Salida:
./main.go:6:6: declared and not used: i
declared and not used: i dice que i está declarada y no se usa. Quítala: for range 2 {.
Consejo
Para repetir algo una cantidad fija de veces, for range n (o for i := range n, si usas el número)
es la forma más clara: no hay inicio, condición ni paso que escribir mal.
Repetir mientras se cumpla: for como “while”
La papelería le debe 10000 a un proveedor, y le paga 3000 cada mes. ¿En cuántos meses termina?
package main
import "fmt"
func main() {
deuda := 10000
meses := 0
for deuda > 0 {
deuda -= 3000
meses++
}
fmt.Println("Pagada en", meses, "meses")
}Salida:
Pagada en 4 meses
Este for tiene solo la condición: repite el bloque mientras deuda > 0 dé true. La deuda pasa por
7000, 4000, 1000 y -2000. En ese momento, deuda > 0 da false y el bucle termina, con
meses en 4. Al cuarto mes le basta con pagar 1000, pero el programa solo cuenta los meses.
Como en el for de tres partes, la condición se revisa antes de cada vuelta. Si da false desde el
principio, el bloque no se ejecuta ni una vez: con deuda := 0, el programa imprime
Pagada en 0 meses.
Úsalo cuando no sabes de antemano cuántas vueltas hacen falta: repite hasta que algo cambie.
Si vienes de JavaScript, Python o Java…
Go no tiene while ni do … while: el for con solo la condición es el while de otros lenguajes.
Con while (deuda > 0) {, el programa no compila.
Un bucle que no termina
Algo dentro del bloque tiene que acercar la condición a false. Si te olvidas de deuda -= 3000, la
deuda sigue en 10000, deuda > 0 da true en cada vuelta y el bucle no termina nunca:
deuda := 10000
meses := 0
for deuda > 0 {
meses++
}Un programa así no tiene error de compilación: compila, se ejecuta y se queda dando vueltas. En tu computadora, seguiría hasta que lo detuvieras a mano. En los ejercicios de este sitio, se detiene después de unos segundos, y la franja de resultado muestra:
- el título “Tu programa no terminó”;
- la explicación “¿Un bucle infinito o un deadlock?”, con el texto “Tu programa tardó más de lo permitido y lo detuvimos. Lo más común es un bucle que nunca termina o goroutines que se esperan entre sí.”;
- la pista “Revisa la condición de salida de tus bucles
fory que cada espera en un channel tenga quién la atienda.”
Una goroutine es una tarea que se ejecuta al mismo tiempo que otras; un channel, una forma de pasar
datos entre goroutines; y un deadlock, un bloqueo en el que varias se esperan entre sí. Los verás en
el bloque 7, de concurrencia. Por ahora, si ves ese mensaje, busca un bucle cuya condición nunca llega
a false.
Si el bucle imprime en cada vuelta, lo más probable es que el sitio lo detenga por imprimir demasiado: la franja muestra el título “Demasiada salida” y la explicación “Tu programa imprimió demasiado”.
Un bucle sin condición: for y break
La caja de la papelería suma precios hasta que la persona que cobra escribe un 0:
package main
import "fmt"
func main() {
total := 0
for {
var precio int
fmt.Scan(&precio)
if precio == 0 {
break
}
total += precio
}
fmt.Println("Total:", total)
}Entrada:
1200 850 3000 0
Salida:
Total: 5050
Un for sin nada antes de la { no tiene condición: repite el bloque para siempre. Para salir está
break (“romper”): termina el bucle en ese momento, y el programa sigue después de la } del for.
En cada vuelta, el programa lee un precio. Si es 0, break termina el bucle antes de sumar. Si no,
lo suma a total y pasa a la vuelta siguiente. Lee 1200, 850 y 3000, y con el 0 sale: el total
es 5050.
Aquí la decisión de salir va en el medio de la vuelta: primero hay que leer el precio, y solo
entonces se sabe si hay que seguir. Un for con condición la revisa al principio de la vuelta, cuando
todavía no se leyó nada: habría que leer un precio antes del bucle y otro al final de cada vuelta.
break funciona en todas las formas de for, no solo en esta. Siempre termina el for que lo encierra,
aunque esté dentro de un if.
Consejo
Si la condición para seguir se puede revisar al principio de cada vuelta, escríbela en el for:
for deuda > 0 {. Deja for sin condición y break para cuando la decisión de salir va en el medio
o al final de la vuelta.
Saltar una vuelta: continue
La papelería reparte pedidos todos los días, salvo los domingos. En sus primeros 10 días, el único domingo es el día 7:
package main
import "fmt"
func main() {
for dia := 1; dia <= 10; dia++ {
if dia%7 == 0 {
continue
}
fmt.Print(dia, " ")
}
fmt.Println()
}Salida:
1 2 3 4 5 6 8 9 10
continue (“continuar”) termina la vuelta actual y salta a la siguiente: el resto del bloque no se
ejecuta en esa vuelta. Con dia en 7, dia%7 == 0 da true (el resto de dividir 7 entre 7 es
0), y continue salta el fmt.Print: el 7 no aparece.
fmt.Print(dia, " ") imprime el día y un espacio, sin terminar la línea; el fmt.Println() del final
la termina.
A diferencia de break, el bucle no termina. En un for de tres partes, continue también ejecuta
el paso, dia++, antes de revisar la condición otra vez: por eso sigue con el día 8.
En un for con solo la condición no hay paso, y ahí continue tiene una trampa. Si saltas la línea
que cambia la variable de la condición, la vuelta siguiente es igual a la anterior:
dia := 1
for dia <= 10 {
if dia%7 == 0 {
continue
}
dia++
}Al llegar a 7, continue salta el dia++: dia se queda en 7 para siempre, y es otro bucle que
no termina.
Consejo
Un continue al principio del bloque sirve para descartar los casos que no interesan, como el domingo.
Así, el resto del bloque trabaja solo con los que sí, sin otro nivel de sangría dentro de un if.
En resumen
stock += 12esstock = stock + 12, y existe para cada operador aritmético que ya conoces.ventas++suma 1 yventas--resta 1: son instrucciones solas, no valores.for inicio; condición; paso { … }es el bucle de tres partes. La variable del inicio existe solo dentro delfor.for i := range nrepite el bloquenveces, conide0an - 1(desde Go 1.22). Sin la variable,for range n.for condición { … }es el “while” de Go.for { … }repite para siempre, hasta unbreak. Si la condición nunca llega afalse, el bucle no termina, y el sitio lo detiene a los pocos segundos.breaktermina el bucle;continuesalta a la vuelta siguiente.
Ejercicios
Completa la función cajasEnPila, que devuelve cuántas cajas tiene una pila de filas filas: 1 caja en la fila 1, 2 en la fila 2, y así.
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"
// cajasEnPila devuelve cuántas cajas hay en una pila de filas filas:
// la fila 1 tiene 1 caja, la fila 2 tiene 2, y así hasta abajo.
func cajasEnPila(filas int) int {
total := 0
for fila := 1; fila <= filas; fila++ {
// Escribe aquí: suma a total las cajas de esta fila.
}
return total
}
func main() {
fmt.Println("Pila de 3 filas:", cajasEnPila(3))
fmt.Println("Pila de 4 filas:", cajasEnPila(4))
}