Una función variádica acepta cero, uno o más valores como único argumento. Si bien las funciones variádicas son atípicas, pueden utilizarse para que su código sea más limpio y legible.
Las funciones variádicas son más comunes de lo que parecen. La más común es la función Println
del paquete fmt
.
func Println(a ...interface{}) (n int, err error)
Una función con un parámetro precedido por una serie de elipses (...
) se considera como una función variádica. La ellipsis implica que el parámetro que se proporciona puede ser cero, uno o más valores. En el paquete fmt.Println
, se indica que el parámetro a
es variádico.
Crearemos un programa que utilice la función fmt.Println
y que pase cero, uno o más valores:
package main
import "fmt"
func main() {
fmt.Println()
fmt.Println("one")
fmt.Println("one", "two")
fmt.Println("one", "two", "three")
}
La primera vez que invocamos fmt.Println
, no pasamos argumentos. La segunda vez que invocamos fmt.Println
solo se ejecuta un único argumento con el valor de one
. Luego pasaremos one
y two
, y por último one
, two
y three
.
Ejecutaremos el programa con el siguiente comando:
- go run print.go
Veremos el siguiente resultado:
Output
one
one two
one two three
La primera línea del resultado está vacía. Esto se debe a que no aprobamos argumentos la primera vez que se invocó a fmt.Println
. La segunda vez se imprime el valor one
. Luego se imprime one
y two
, y por último one
, two
y three
.
Ahora que vimos la forma de invocar una función variádica, veremos la forma de definir una propia.
Podemos definir una función variádica usando puntos suspensivos (...
) en frente del argumento. Crearemos un programa que salude a la gente cuando se envíen sus nombres a la función:
package main
import "fmt"
func main() {
sayHello()
sayHello("Sammy")
sayHello("Sammy", "Jessica", "Drew", "Jamie")
}
func sayHello(names ...string) {
for _, n := range names {
fmt.Printf("Hello %s\n", n)
}
}
Creamos la función sayHello
que solo toma un único parámetro llamado names
. El parámetro es variádico, ya que disponemos puntos suspensivos (...
) antes del tipo de datos: ...string
. Esto indica a Go que la función puede aceptar cero, uno o muchos argumentos.
La función sayHello
recibe el parámetro names
como un slice
. Debido a que el tipo de datos es un string
, el parámetro names
puede tratarse como un segmento de cadenas ([]string
) dentro del cuerpo de la función. Podemos crear un bucle con el operador range
e iterar el segmento de cadenas.
Si ejecutamos el programa, obtendremos el siguiente resultado:
OutputHello Sammy
Hello Sammy
Hello Jessica
Hello Drew
Hello Jamie
Observe que no hubo impresiones la primera vez que invocamos a sayHello
. Esto se debe a que el parámetro variádico fue un slice
vacío de string
. Debido a que repetimos el segmento, no hay elementos para iterar y nunca se invoca a fmt.Printf
.
Modificaremos el programa para detectar que no se enviaron valores:
package main
import "fmt"
func main() {
sayHello()
sayHello("Sammy")
sayHello("Sammy", "Jessica", "Drew", "Jamie")
}
func sayHello(names ...string) {
if len(names) == 0 {
fmt.Println("nobody to greet")
return
}
for _, n := range names {
fmt.Printf("Hello %s\n", n)
}
}
Ahora, usando una instrucción if
, si no se pasan valores la extensión de names
será 0
e imprimiremos nobody to greet
:
Outputnobody to greet
Hello Sammy
Hello Sammy
Hello Jessica
Hello Drew
Hello Jamie
El uso de un parámetro de variable puede hacer que su código sea más legible. Crearemos una función que una las palabras con un delimitador especificado. Primero, crearemos este programa sin una función de variable para mostrar cómo se leería:
package main
import "fmt"
func main() {
var line string
line = join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"})
fmt.Println(line)
line = join(",", []string{"Sammy", "Jessica"})
fmt.Println(line)
line = join(",", []string{"Sammy"})
fmt.Println(line)
}
func join(del string, values []string) string {
var line string
for i, v := range values {
line = line + v
if i != len(values)-1 {
line = line + del
}
}
return line
}
En este programa, pasaremos una coma (,
) como delimitador a la función join
. Luego, pasaremos un segmento de valores que se unirán. Este es el resultado:
OutputSammy,Jessica,Drew,Jamie
Sammy,Jessica
Sammy
Debido a que la función toma un segmento de cadena como parámetro values
, debimos ajustar todas nuestras palabras en un segmento cuando invocamos la función join
. Esto puede dificultar la lectura del código.
Ahora, escribiremos la misma función, pero usaremos una función variádica:
package main
import "fmt"
func main() {
var line string
line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
fmt.Println(line)
line = join(",", "Sammy", "Jessica")
fmt.Println(line)
line = join(",", "Sammy")
fmt.Println(line)
}
func join(del string, values ...string) string {
var line string
for i, v := range values {
line = line + v
if i != len(values)-1 {
line = line + del
}
}
return line
}
Si ejecutamos el programa, podemos ver que obtenemos el mismo resultado que el programa anterior:
OutputSammy,Jessica,Drew,Jamie
Sammy,Jessica
Sammy
Si bien ambas versiones de la función join
hacen exactamente lo mismo mediante programación, la versión variádica de la función es mucho más fácil de leer cuando se invoca.
Solo puede disponer de un parámetro variádico en una función y debe ser el último parámetro definido en esta. La definición de parámetros en una función variádica en cualquier orden que no sea el último parámetro dará como resultado un error de compilación:
package main
import "fmt"
func main() {
var line string
line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
fmt.Println(line)
line = join(",", "Sammy", "Jessica")
fmt.Println(line)
line = join(",", "Sammy")
fmt.Println(line)
}
func join(values ...string, del string) string {
var line string
for i, v := range values {
line = line + v
if i != len(values)-1 {
line = line + del
}
}
return line
}
Esta vez, disponemos el parámetro values
primero en la función join
. Esto provocará el siguiente error de compilación:
Output./join_error.go:18:11: syntax error: cannot use ... with non-final parameter values
Cuando se define cualquier función variádica, solo el último parámetro puede ser variádico.
Hasta ahora, vimos que podemos pasar cero, uno o más valores a una función variádica. Sin embargo, habrá ocasiones en que dispongamos de un segmento de valores y queramos enviarlos a una función variádica.
Veamos nuestra función join
de la última sección para ver qué sucede:
package main
import "fmt"
func main() {
var line string
names := []string{"Sammy", "Jessica", "Drew", "Jamie"}
line = join(",", names)
fmt.Println(line)
}
func join(del string, values ...string) string {
var line string
for i, v := range values {
line = line + v
if i != len(values)-1 {
line = line + del
}
}
return line
}
Si ejecutamos este programa, veremos un error de compilación:
Output./join-error.go:10:14: cannot use names (type []string) as type string in argument to join
Aunque la función variable convertirá el parámetro values ...string
en un segmento de cadenas strings[]
, no podemos pasar un segmento de cadenas como argumento. Esto se debe a que el compilador espera argumentos discretos de cadenas.
Para hallar una solución a esto, podemos explandir un factor logístico agregándole como sufijo un conjunto de puntos suspensivos (...
) y convertirlo en argumentos discretos que se pasarán a una función variádica:
package main
import "fmt"
func main() {
var line string
names := []string{"Sammy", "Jessica", "Drew", "Jamie"}
line = join(",", names...)
fmt.Println(line)
}
func join(del string, values ...string) string {
var line string
for i, v := range values {
line = line + v
if i != len(values)-1 {
line = line + del
}
}
return line
}
Esta vez, cuando llamamos a la función join
, expandimos el segmento names
agregando puntos suspensivos (...
).
Esto permite que el programa ahore funcione como se espera:
OutputSammy,Jessica,Drew,Jamie
Es importante tener en cuenta que de todas formas podemos pasar cero, uno o más argumentos y un segmento que expandamos. A continuación, se ofrece el código que pasa todas las variaciones que hemos visto hasta ahora:
package main
import "fmt"
func main() {
var line string
line = join(",", []string{"Sammy", "Jessica", "Drew", "Jamie"}...)
fmt.Println(line)
line = join(",", "Sammy", "Jessica", "Drew", "Jamie")
fmt.Println(line)
line = join(",", "Sammy", "Jessica")
fmt.Println(line)
line = join(",", "Sammy")
fmt.Println(line)
}
func join(del string, values ...string) string {
var line string
for i, v := range values {
line = line + v
if i != len(values)-1 {
line = line + del
}
}
return line
}
OutputSammy,Jessica,Drew,Jamie
Sammy,Jessica,Drew,Jamie
Sammy,Jessica
Sammy
De esta manera, sabrá pasar cero, uno o muchos argumentos, y un segmento que expanda, a una función variádica.
En este tutorial, vio cómo las funciones variádicas pueden limpiar su código. Si bien no siempre necesitará usarlas, pueden resultarle útiles:
Para obtener más información sobre la creación e invocación de funciones, puede leer Cómo definir e invocar funciones en Go.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!