Esta pagina se ve mejor con JavaScript habilitado

Funciones en Go

 ·  🎃 kr0m

En un artículo anterior ya vimos la bases del lenguaje Go basics , en esta ocasión profundizaremos en las funciones de Go.
- Declaración de funciones.
- Múltiples valores de retorno.
- Funciones como tipos.
- Recursividad.
- Métodos.
- Funciones con entrada variable.


Si eres nuevo en el mundo de Go te recomiendo los siguientes artículos anteriores:


Declaración de funciones:

Dentro del paréntesis se indican los tipos de las variables de entrada.

package main

import "fmt"

func repeat(word string, reps int) {
    for i := 0; i < reps; i++ {
        fmt.Println(word)
    }
}

func main() {
    repeat("AlfaExploit", 5)
}

Múltiples valores de retorno:

Es similar a retornar un array de valores.

package main

import "fmt"

func divisionAndRemainder(dividend, divisor int) (int, int) {
    quotient := dividend / divisor
    remainder := dividend % divisor
    return quotient, remainder
}

func main() {
    quotient, remainder := divisionAndRemainder(10, 3)
    fmt.Println(quotient, remainder)
}

Funciones como tipos:

Se trata de utilizar una variable a modo de función, en este ejemplo la variable operation se comporta como si de la función multiplication se tratase. Esto puede resultar muy útil cuando hagamos tests unitarios mas adelante.

package main

import "fmt"

type Operation func(int, int) int

func multiplication(a, b int) int {
    return a * b
}

func main() {
    var operation Operation = multiplication
    result := operation(2, 3)
    fmt.Println(result)
}

Recursividad:

La recursión puede consumir mas recursos que las soluciones no recursivas, pero simplifican el código.

Un ejemplo básico sería calcular el factorial de un número dado.

package main

import "fmt"

func factorial(n int) int {
    if n <= 1 {
        return 1
    }
    return n * factorial(n-1)
}

func main() {
    result := factorial(3)
    fmt.Println(result)
}

Como ejemplo pongamos que n vale 3 por lo tanto el código de la función factorial que se llama a si misma equivaldría a calcular:

n * factorial(n-1)
factorial(3): 3 * factorial(3-1) -> 3 * factorial(2)
factorial(2): 2 * factorial(2-1) -> 2 * factorial(1)
factorial(1): 1

Por lo tanto podemos ver que:

factorial(3): 3 * factorial(2)
factorial(2): 2 * factorial(1)
factorial(1): 1

Por lo tanto sustituyendo nos queda:

factorial(3): 3 * factorial(2)
factorial(2): 2 * 1

Y finalmente:

factorial(3): 3 * 2 * 1

Otro ejemplo realmente útil es cuando necesitamos listar un árbol de directorios:

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "path/filepath"
)

func scanDirectory(path string) error {
    fmt.Println(path)
    files, err := ioutil.ReadDir(path)
    if err != nil {
        fmt.Printf("Returning error from scanDirectory(\"%s\") call\n", path)
        return err
    }
    for _, file := range files {
        filePath := filepath.Join(path, file.Name())
        if file.IsDir() {
            err := scanDirectory(filePath)
            if err != nil {
                fmt.Printf("Returning error from scanDirectory(\"%s\") call\n", path)
                return err
            }
        } else {
            fmt.Println(filePath)
        }
    }
    return nil
}

func main() {
    err := scanDirectory("my_huge_directory")
    if err != nil {
        log.Fatal(err)
    }
}

Métodos:

Go no es un lenguaje orientado a objetos propiamente dicho, pero soporta encapsulación, interfaces y métodos. En esta sección nos centraremos en esto último para comprender como vincular métodos a tipos.

Los métodos presentan varias ventajas respecto a utilizar funciones:

  • Los métodos quedan vinculados de forma lógica a los tipos.
  • Los nombres de los métodos pueden ser reutilizados en los distintos tipos.

En este ejemplo vincularemos un tipo de tipo estructura al método area(), en dicho método obtendremos una copia del objeto en si mismo, Go lo llama receiver en algunos lenguajes de programación lo llaman this, la forma idiomática de nombrar a dicho receiver es utilizar la primera letra del tipo del objeto.

package main

import "fmt"

type rectangle struct {
    Base   int
    Height int
}

func (r rectangle) area() int {
    return r.Base * r.Height
}

func main() {
    // Assign structure variable values
    rect := rectangle{Base: 3, Height: 4}
    // rect structure moreover has area method available
    // Call structure method
    area := rect.area()
    fmt.Println(area)
}

Los métodos no tienen porque limitarse a métodos de estructuras, también es posible vincularlos a tipos primitivos.
Para ello debemos crear un alias del tipo, en este caso myInt y asignarlo como tipo del receiver del método, en este caso además hemos definido el parámetro de entrada de la función y el valor de retorno de este mismo tipo. Veamos el siguiente ejemplo.

package main

import "fmt"

type myInt int

func (a myInt) add(b myInt) myInt {
    return a + b
}

func main() {
    num1 := myInt(5)
    num2 := myInt(10)
    sum := num1.add(num2)
    fmt.Println("Sum is: ", sum)
}

Funciones con entrada variable:

Una función variadic es aquella que tiene un número de parámetros de entrada variable, los únicos requisitos son que todas las variables variadic sean del mismo tipo y esta sea la última de la lista de argumentos.

Estas variables reciben los argumentos separados por coma y los convierten a un array de elementos. Podemos ver su funcionamiento en el siguiente ejemplo.

package main

import "fmt"

func addition(nums ...int) int {
    fmt.Println(nums)
    result := 0
    for _, num := range nums {
        result += num
    }
    return result
}

func main() {
    total := addition(1, 2, 3)
    fmt.Println(total)

    total = addition(1, 2, 3, 4)
    fmt.Println(total)
}

Si por el contrario tenemos un slice y queremos pasárselo a la función, debemos indicárselo para que en la función no se haga una doble conversión de array a array.

package main

import "fmt"

func addition(nums ...int) int {
    fmt.Println(nums)
    result := 0
    for _, num := range nums {
        result += num
    }
    return result
}

func main() {
    testSlice := []int{1, 2, 3}
    total := addition(testSlice...)
    fmt.Println(total)

    testSlice = []int{1, 2, 3, 4}
    total = addition(testSlice...)
    fmt.Println(total)
}
Si te ha gustado el artículo puedes invitarme a un RedBull aquí