Cómo configurar y invocar funciones en Go

Introducción

Una función es una sección de código que, una vez definida, se puede volver a utilizar. Las funciones se utilizan para facilitar más la comprensión de su código mediante su división en tareas pequeñas y comprensibles que se pueden utilizar más de una vez en su programa.

Go se entrega con una potente biblioteca estándar que cuenta con muchas funciones predefinidas. Las siguientes son algunas que probablemente ya conozca del paquete fmt:

  • fmt.Println(), que imprime objetos en una salida estándar (en mayoría de los casos, su terminal).
  • fmt.Printf(), que le permite dar formato a su resultado impreso.

Los nombres de las funciones incluyen paréntesis y pueden contener parámetros.

En este tutorial, repasaremos la forma de definir sus propias funciones para utilizarlas en sus proyectos de codificación.

Cómo definir una función

Comencemos convirtiendo el clásico programa “Hello, World!” en una función.

Crearemos un nuevo archivo de texto en nuestro editor de texto preferido e invocaremos el programa hello.go. A continuación, definiremos la función.

Las funciones se definen usando la palabra clave func. Luego, se muestra un nombre que usted elige y un conjunto de paréntesis que contienen cualquier parámetro que la función tome (pueden estar vacíos). Las líneas de código de las funciones se escriben entre llaves {}.

En este caso, definiremos una función llamada hello():

hello.go

func hello() {} 

Con esto, se establece la instrucción inicial para crear una función.

A partir de aquí, añadiremos una segunda línea para proporcionar las instrucciones de lo que hace la función. En este caso, imprimiremos Hello, World! en la consola:

hello.go

func hello() {     fmt.Println("Hello, World!") } 

Ahora, nuestra función está definida por completo, pero si ejecutamos el programa en este momento no ocurrirá nada porque no invocamos la función.

Por lo tanto, dentro de nuestro bloque main() de la función, invocaremos la función con hello():

hello.go

package main  import "fmt"  func main() {     hello() }  func hello() {     fmt.Println("Hello, World!") } 

Ahora, ejecutaremos el programa:

  • go run hello.go

Obtendrá el siguiente resultado:

OutputHello, World! 

Observe que también implementamos una función llamada main(). main() es una función especial que indica al compilador que este es el punto en el que se debe** iniciar** el programa. Para cualquier programa que quiera que sea ejecutable (un programa que pueda ejecutarse desde la línea de comandos), necesitará una función main(). La función main() debe aparecer una sola vez, debe estar en el paquete main() y no debe recibir ni mostrar argumentos. Esto permite la ejecución del programa en cualquier programa de Go. Como indica el siguiente ejemplo:

main.go

package main  import "fmt"  func main() {     fmt.Println("this is the main section of the program") } 

Las funciones pueden ser más complejas que la función hello() que definimos. Entre otras opciones, podemos usar bucles for e instrucciones condicionales dentro de nuestro bloque de función.

Por ejemplo, la siguiente función utiliza una instrucción condicional para verificar si la entrada de la variable name contiene una vocal. Luego, emplea un bucle for para la iteración sobre las letras de la cadena name.

names.go

package main  import (     "fmt"     "strings" )  func main() {     names() }  func names() {     fmt.Println("Enter your name:")      var name string     fmt.Scanln(&name)     // Check whether name has a vowel     for _, v := range strings.ToLower(name) {         if v == 'a' || v == 'e' || v == 'i' || v == 'o' || v == 'u' {             fmt.Println("Your name contains a vowel.")             return         }     }     fmt.Println("Your name does not contain a vowel.") } 

La función name() que definimos aquí configura una variable name con entrada y, luego, establece una instrucción condicional dentro de un bucle for. Esto indica cómo se puede organizar el código dentro de la definición de una función. Sin embargo, dependiendo de lo que pretendamos de nuestro programa y de la forma en que queramos configurar nuestro código, tal vez nos convenga definir la instrucción condicional y el bucle for como dos funciones separadas.

La definición de funciones dentro de un programa hace que nuestro código sea modular y reutilizable para que podamos invocar a las mismas funciones sin tener que volver a escribirlas.

Trabajar con parámetros

Hasta ahora, examinamos funciones con paréntesis vacíos que no toman argumentos, pero podemos definir parámetros en las definiciones de las funciones dentro de sus paréntesis.

Un parámetro es una entidad con nombre en la definición de una función que especifica un argumento que la función puede aceptar. En Go, debe especificar el tipo de datos para cada parámetro.

Vamos a crear un programa que repite una palabra una cantidad de veces especificada. Tomará un parámetro string llamado word y un parámetro int llamado reps que indicará la cantidad de veces que se debe repetir la palabra.

repeat.go

package main  import "fmt"  func main() {     repeat("Sammy", 5) }  func repeat(word string, reps int) {     for i := 0; i < reps; i++ {         fmt.Print(word)     } } 

Establecimos el valor Sammy para el parámetro word y el valor 5 para el parámetro reps. Estos valores corresponden a cada parámetro en el orden en el que se proporcionaron. La función repeat tiene un bucle for que ​​se repetirá la cantidad de veces especificada en el parámetro reps. En cada repetición, se imprime el valor del parámetro word.

Este es el resultado del programa:

OutputSammySammySammySammySammy 

Si tiene un conjunto de parámetros con el mismo valor, puede omitir la repetición de la especificación de tipo. Crearemos un pequeño programa que tenga en cuenta los parámetros x, y y z, todos ellos valores int. Crearemos una función que añada los parámetros juntos en diferentes configuraciones. La función imprimirá la suma de ellas. Luego, invocaremos la función y le pasaremos números.

add_numbers.go

package main  import "fmt"  func main() {     addNumbers(1, 2, 3) }  func addNumbers(x, y, z int) {     a := x + y     b := x + z     c := y + z     fmt.Println(a, b, c) } 

Cuando creamos la firma de la función addNumbers, no necesitamos volver a especificar el tipo; solo lo hicimos al final.

Pasamos el número 1 para el parámetro x, el 2 para el y y el 3 para el z. Estos valores corresponden a cada parámetro en el orden en el que se proporcionan.

El programa realiza los siguientes cálculos sobre la base de los valores que pasamos a los parámetros:

a = 1 + 2 b = 1 + 3 c = 2 + 3 

La función también imprime a, b y c, y según este cálculo esperamos que a sea igual a 3, b a 4, y c a 5. Ejecutaremos el programa:

  • go run add_numbers.go
Output3 4 5 

Cuando pasamos 1, 2 y 3 como parámetros a la función addNumbers(), recibimos el resultado esperado.

Los parámetros son argumentos que suelen definirse como variables en las definiciones de las funciones. Les puede asignar valores cuando ejecute el método y pasar los argumentos a la función.

Mostrar un valor

Puede pasar un valor de un parámetro a una función y una función también puede producir un valor.

Una función puede producir un valor con la instrucción return, que producirá el cierre de una función y, de forma opcional, pasará una expresión al autor de la llamada. El tipo de datos que mostrado también debe especificarse.

Hasta ahora, usamos la instrucción fmt.Println() en lugar de return en nuestras funciones. Crearemos un programa que, en lugar de generar impresiones, mostrará una variable.

En un nuevo archivo de texto llamado double.go, crearemos un programa que duplicará el parámetro x y mostrará la variable y. Realizaremos una invocación para imprimir la variable result, que se forma ejecutando la función double() con el valor 3 pasado:

double.go

package main  import "fmt"  func main() {     result := double(3)     fmt.Println(result) }  func double(x int) int {     y := x * 2     return y }  

Podemos ejecutar el programa y ver el resultado:

  • go run double.go
Output6 

El número entero 6 se muestra como resultado, que es lo esperado al multiplicar 3 por 2.

Si una función especifica una devolución, debe mostrarla como parte del código. Si no lo hace, verá un error de compilación.

Podemos demostrar esto excluyendo la línea con la instrucción return:

double.go

package main  import "fmt"  func main() {     result := double(3)     fmt.Println(result) }  func double(x int) int {     y := x * 2     // return y }  

Ahora, ejecutaremos el programa de nuevo:

  • go run double.go
Output./double.go:13:1: missing return at end of function 

Sin usar la instrucción return aquí, el programa no puede realizar la compilación.

Las funciones se cerrarán de inmediato cuando hallen una instrucción return, aun cuando no se encuentre al final de la función:

return_loop.go

package main  import "fmt"  func main() {     loopFive() }  func loopFive() {     for i := 0; i < 25; i++ {         fmt.Print(i)         if i == 5 {             // Stop function at i == 5             return         }     }     fmt.Println("This line will not execute.") } 

Aquí, iteramos un bucle for, e indicamos a este que ejecute 25 iteraciones. Sin embargo, dentro del bucle for, hay una instrucción condicional if que comprueba si el valor de i es igual a 5. Si lo es, emitiremos una instrucción return. Debido a que se trata de la función loopFive, una instrucción return en cualquier punto de la función la cerrará. Como resultado, nunca llegamos a la última línea de esta función para imprimir la instrucción. This line will not execute..

El uso de la instrucción return dentro del bucle for finaliza la función, por lo que la línea que se encuentra fuera del bucle no se ejecutará. Si, en su lugar, hubiéramos usado una instrucción break, solo el bucle se habría cerrado en ese momento y la última línea fmt.Println() se habría ejecutado.

La instrucción return cierra una función y puede mostrar un valor si se especifica en la firma de la función.

Mostrar varios valores

Se puede especificar más de un valor de devolución para una función. Examinaremos el programa repeat.go y haremos que muestre dos valores. El primero será el valor repetido y el segundo será un error si el parámetro reps no es un valor superior a 0:

repeat.go

package main  import "fmt"  func main() {     val, err := repeat("Sammy", -1)     if err != nil {         fmt.Println(err)         return     }     fmt.Println(val) }  func repeat(word string, reps int) (string, error) {     if reps <= 0 {         return "", fmt.Errorf("invalid value of %d provided for reps. value must be greater than 0.", reps)     }     var value string     for i := 0; i < reps; i++ {         value = value + word     }     return value, nil } 

Lo primero que hace la función repeat es verificar si el argumento reps es un valor válido. Cualquier valor que no sea superior a 0 provocará un error. Dado a que pasamos el valor -1, esta rama del código se ejecutará. Tenga en cuenta que cuando se realiza la devolución desde la función, se deben proporcionar los valores de devolución string y error. Debido a que los argumentos proporcionados produjeron un error, pasaremos una cadena en blanco para el primer valor mostrado y el error para el segundo.

En la función main(), podemos recibir los dos valores de devolución declarando dos variables nuevas: value y err. Debido a que podría haber un error en la devolución, verificaremos si recibimos un error antes de continuar con nuestro programa. En este ejemplo, recibimos un error. Imprimiremos el error y aplicaremos return para la función main() a fin de cerrar el programa.

Si no se produjo un error, imprimiremos el valor de devolución de la función.

Nota: Se recomienda mostrar solo dos o tres valores. Además, todos los errores se deben mostrar siempre como el último valor de devolución de una función.

Al ejecutar el programa, se obtendrá el siguiente resultado:

Outputinvalid value of -1 provided for reps. value must be greater than 0. 

En esta sección, analizamos la manera de usar la instrucción return para mostrar varios valores de una función.

Conclusión

Las funciones son bloques de código de instrucciones que realizan acciones en un programa, lo cual contribuye a que nuestro código sea reutilizable y modular.

Para obtener más información sobre cómo hacer que su código sea más modular, puede leer nuestra guía sobre Cómo escribir paquetes en Go.