Tracks
/
Go
Go
/
Ejercicios
/
Maestro de lasañas
Maestro de lasañas

Maestro de lasañas

Ejercicio de aprendizaje

Introducción

Una función te permite agrupar código en una unidad reutilizable. Consiste en 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.

Parámetros de funciones

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 predeterminados, 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 sola declaración de tipo.

import "fmt"

func PrintGreetingName(greeting, name string) {
  fmt.Println(greeting + " " + name)
}

Parámetros vs. argumentos

Cubramos 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 invocamos la función. 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")

Valores de retorno

A los parámetros de la función les siguen cero o más valores de retorno, que también deben tener un tipo explícito. Los valores de retorno únicos se dejan sin paréntesis; los múltiples se envuelven entre paréntesis. Las funciones devuelven valores al código que las llama usando la palabra clave return. Puede haber varias sentencias return en una función. La ejecución de la función termina en cuanto llega a una de esas sentencias return. Si una función debe devolver varios valores, estos se separan con comas.

func Hello(name string) string {
  return "Hello " + name
}

func HelloAndGoodbye(name string) (string, string) {
  return "Hello " + name, "Goodbye " + name
}

Invocar funciones

Para invocar una función, se especifica el nombre de la función y se pasan argumentos para cada uno de los parámetros de la función 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)

Valores de retorno con nombre y retorno desnudo

Además de los parámetros, los valores de retorno también pueden tener nombre. Si se usan valores de retorno con nombre, una sentencia return sin argumentos devolverá esos valores. Esto se conoce como un retorno «desnudo».

func SumAndMultiplyThenMinus(a, b, c int) (sum, mult int) {
    sum, mult = a+b, a*b
    sum -= c
    mult -= c
    return
}

Paso por valor vs. paso por referencia

También es importante aclarar el concepto de pasar por valor y pasar por referencia.

Primero, aclaremos 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. Detrás de escena, Go hace básicamente una copia del valor original para que solo esta copia, también conocida como v, sea la que modifica la función.

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 le proporcionan 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, también conocido como paso por referencia.

Punteros

Usamos un pointer para lograr el paso por referencia. Al pasar argumentos pointer a una función, podemos modificar los datos subyacentes que se pasan a la función en lugar de operar solo sobre una copia de los datos.

Por ahora, basta con saber que los tipos puntero se reconocen por el * delante del 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 en el temario tenemos una sección dedicada a ayudarte a dominar los punteros.

Excepciones

Ten en cuenta que slices y maps son excepciones a la regla mencionada arriba. 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 verán reflejados en el slice o map original.

Instrucciones

En este ejercicio vas a escribir un poco más de código relacionado con la preparación y la cocción de esa magnífica lasaña de tu libro de cocina favorito.

Tienes cuatro tareas. La primera está relacionada con la cocción en sí, y las otras tres tienen que ver con la preparación perfecta.

1. Estima el tiempo de preparación

Para la próxima lasaña que prepares, quieres asegurarte de reservar suficiente tiempo para poder disfrutar de la cocción. Ya planeaste qué capas tendrá tu lasaña. Ahora quieres estimar cuánto tardará la preparación según eso.

Implementa una función PreparationTime que acepte un slice de capas como un []string y el tiempo de preparación promedio por capa en minutos como un int. La función debe devolver la estimación del tiempo total de preparación según la cantidad de capas, como un int. Go no tiene valores predeterminados para las funciones. Si el tiempo de preparación promedio se pasa como 0 (el valor inicial predeterminado de un int), entonces se debe usar el valor predeterminado de 2.

layers := []string{"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"}
PreparationTime(layers, 3)
// => 18
PreparationTime(layers, 0)
// => 12

2. Calcula las cantidades de fideos y salsa necesarias

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 en tu lasaña, necesitarás 50 gramos de fideos. Por cada capa de salsa en tu lasaña, necesitarás 0,2 litros de salsa.

Define la función Quantities que recibe un slice de capas como parámetro, de tipo []string. Luego, la función determinará 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

3. Agrega el ingrediente secreto

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 agregar 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 con 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 un puntero, los cambios a los dos argumentos de tipo []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"}

4. Escala la receta

Las cantidades que figuran en tu libro de cocina solo alcanzan para dos porciones de lasaña. Como la próxima vez quieres cocinar para más personas, quieres calcular las cantidades para diferentes números de porciones.

Implementa una función ScaleRecipe que recibe dos parámetros.

  • Un slice de cantidades float64 necesarias para 2 porciones.
  • La cantidad de porciones que quieres cocinar.

La función debe devolver un slice de float64 con las cantidades necesarias para la cantidad de porciones deseada. 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 }
Editar en GitHub El enlace se abre en una ventana o una pestaña nuevas
Go Exercism

¿Todo listo para empezar Maestro de lasañas?

Regístrate en Exercism para aprender y dominar Go con 34 conceptos166 ejercicios y mentoría humana real, todo gratis.