Una función te permite agrupar código en una unidad reutilizable.
Consta de la palabra clave func, el nombre de la función y una lista, separada por comas, de cero o más parámetros y tipos entre paréntesis.
Todos los parámetros deben tener un tipo explícito; no hay inferencia de tipos para los parámetros. Los parámetros no tienen valores por defecto, así que todos los parámetros de una función son obligatorios.
import "fmt"
// No parameters
func PrintHello() {
fmt.Println("Hello")
}
// Two parameters
func PrintGreetingName(greeting string, name string) {
fmt.Println(greeting + " " + name)
}
Los parámetros del mismo tipo se pueden declarar juntos, seguidos de una única declaración de tipo.
import "fmt"
func PrintGreetingName(greeting, name string) {
fmt.Println(greeting + " " + name)
}
Vamos a repasar rápidamente dos términos que a menudo se confunden: parameters y arguments.
Los parámetros de una función son los nombres definidos en la firma de la función, como greeting y name en la función PrintGreetingName de arriba.
Los argumentos de una función son los valores concretos que se pasan a los parámetros de la función cuando la invocamos.
Por ejemplo, en el ejemplo de abajo, "Hello" y "Katrina" son los argumentos que se pasan a los parámetros greeting y name:
PrintGreetingName("Hello", "Katrina")
A los parámetros de la función les siguen cero o más valores devueltos, que también deben tener un tipo explícito.
Un único valor devuelto se escribe tal cual; varios valores devueltos se envuelven entre paréntesis.
Los valores se devuelven al código que hizo la llamada usando la palabra clave return.
Puede haber varias instrucciones return en una función.
La ejecución de la función termina en cuanto se topa con una de esas instrucciones return.
Si una función debe devolver varios valores, estos se separan por comas.
func Hello(name string) string {
return "Hello " + name
}
func HelloAndGoodbye(name string) (string, string) {
return "Hello " + name, "Goodbye " + name
}
Para invocar una función, se especifica el nombre de la función y se pasan argumentos para cada uno de sus parámetros entre paréntesis.
import "fmt"
// No parameters, no return value
func PrintHello() {
fmt.Println("Hello")
}
// Called like this:
PrintHello()
// One parameter, one return value
func Hello(name string) string {
return "Hello " + name
}
// Called like this:
greeting := Hello("Dave")
// Multiple parameters, multiple return values
func SumAndMultiply(a, b int) (int, int) {
return a+b, a*b
}
// Called like this:
aplusb, atimesb := SumAndMultiply(a, b)
Al igual que los parámetros, los valores devueltos pueden tener nombre de forma opcional.
Si se usan valores devueltos con nombre, una instrucción return sin argumentos devolverá esos valores.
Esto se conoce como retorno «desnudo».
func SumAndMultiplyThenMinus(a, b, c int) (sum, mult int) {
sum, mult = a+b, a*b
sum -= c
mult -= c
return
}
También es importante aclarar el concepto de paso por valor y paso por referencia.
Primero, vamos a aclarar el paso por valor.
En el ejemplo de abajo, cuando pasamos la variable val a la función MultiplyByTwo, pasamos una copia de val.
Por eso, newVal tiene el valor actualizado 4, pero la variable original val sigue siendo 2.
En realidad, Go básicamente hace una copia del valor original para que la función solo modifique esa copia, también conocida como v.
val := 2
func MultiplyByTwo(v int) int {
v = v * 2
return v
}
newVal := MultiplyByTwo(val)
// newval is 4, val is still 2 because only a copy of its value was passed into the function
En sentido estricto, en Go todos los argumentos se pasan por valor, es decir, se hace una copia del valor o de los datos que se pasan a la función.
Pero si no quieres hacer una copia de los datos que se pasan a una función y quieres cambiar esos datos dentro de la función,
entonces deberías usar pointers como argumentos, lo que también se conoce como paso por referencia.
Usamos un pointer para conseguir el paso por referencia.
Al pasar argumentos de tipo pointer a una función,
podríamos modificar los datos subyacentes que se le pasan en lugar de operar solo sobre una copia de esos datos.
Por ahora basta con saber que los tipos puntero se reconocen por el * que precede al tipo en la firma de la función.
func HandlePointers(x, y *int) {
// Some logic to handle integer values referenced by pointers x and y
}
Si el concepto de pointer te resulta confuso, no te preocupes.
Más adelante hay una sección dedicada en el temario que te ayudará a dominar los punteros.
Ten en cuenta que slices y maps son excepciones a la regla mencionada antes.
Cuando pasamos un slice o un map como argumentos a una función, se tratan como tipos puntero aunque no haya un * explícito en el tipo.
Esto significa que si pasamos un slice o un map a una función y modificamos sus datos subyacentes,
los cambios se reflejarán en el slice o el map originales.
En este ejercicio vas a escribir algo más de código relacionado con la preparación y el cocinado de tu magnífica lasaña de tu libro de recetas favorito.
Tienes cuatro tareas. La primera está relacionada con el cocinado en sí, y las otras tres tratan sobre la preparación perfecta.
Para la próxima lasaña que prepares, quieres asegurarte de reservar tiempo suficiente para poder disfrutar del cocinado. Ya has planificado qué capas tendrá tu lasaña. Ahora quieres estimar cuánto tardará la preparación en función de eso.
Implementa una función PreparationTime que acepte un slice de capas como un []string y el tiempo medio de preparación por capa en minutos como un int.
La función debe devolver la estimación del tiempo total de preparación en función del número de capas como un int.
Go no tiene valores por defecto para las funciones.
Si el tiempo medio de preparación se pasa como 0 (el valor inicial por defecto de un int), debe usarse el valor por defecto de 2.
layers := []string{"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"}
PreparationTime(layers, 3)
// => 18
PreparationTime(layers, 0)
// => 12
Además de reservar el tiempo, también quieres asegurarte de tener suficiente salsa y fideos para cocinar la lasaña de tus sueños. Por cada capa de fideos de tu lasaña, necesitarás 50 gramos de fideos. Por cada capa de salsa de tu lasaña, necesitarás 0,2 litros de salsa.
Define la función Quantities que toma un slice de capas como parámetro como un []string.
La función determinará entonces la cantidad de fideos y salsa necesarios para preparar tu comida.
El resultado debe devolverse como dos valores: noodles como un int y sauce como un float64.
Quantities([]string{"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"})
// => 100, 0.4
Hace un tiempo visitaste a un amigo y comiste lasaña en su casa. Estaba increíble y tenía algo especial. Tu amigo te envió la lista de ingredientes y te dijo que el último elemento de la lista es el «ingrediente secreto» que hizo que la comida fuera tan especial. Ahora quieres añadir ese ingrediente secreto también a tu receta.
Escribe una función AddSecretIngredient que acepte dos slices de ingredientes de tipo []string como parámetros.
El primer parámetro es la lista que te envió tu amigo, y el segundo es la lista de ingredientes de tu propia receta.
El último elemento de tu lista de ingredientes siempre es "?".
La función debe reemplazarlo por el último elemento de la lista de tu amigo.
Nota: AddSecretIngredient no devuelve nada; debes modificar la lista de tus ingredientes directamente.
La lista con los ingredientes de tu amigo no debe modificarse.
Además, como el slice se pasa a una función como punteros, los cambios en los dos argumentos []string que se pasan a AddSecretIngredient se modificarán directamente.
friendsList := []string{"noodles", "sauce", "mozzarella", "kampot pepper"}
myList := []string{"noodles", "meat", "sauce", "mozzarella","?"}
AddSecretIngredient(friendsList, myList)
// myList => []string{"noodles", "meat", "sauce", "mozzarella", "kampot pepper"}
Las cantidades que figuran en tu libro de cocina solo dan para una lasaña de dos porciones. Como la próxima vez quieres cocinar para más personas, quieres calcular las cantidades para distintos números de porciones.
Implementa una función ScaleRecipe que tome dos parámetros.
float64 necesarias para 2 porciones.La función debe devolver un slice de float64 con las cantidades necesarias para el número de porciones deseado.
Sin embargo, quieres conservar la receta original.
Esto significa que el argumento quantities no debe modificarse en esta función.
quantities := []float64{ 1.2, 3.6, 10.5 }
scaledQuantities := ScaleRecipe(quantities, 4)
// => []float64{ 2.4, 7.2, 21 }
Regístrate en Exercism para aprender y dominar Go con 34 conceptos166 ejercicios y mentoría humana real, todo gratis.