Funciones en Julia (5ª parte – ¡Hola Julia!)

Publicado el 28 julio 2020 por Daniel Rodríguez @analyticslane

A la hora de estructura un programa las funciones son clave, ya que permiten reutilizar pequeños trozos de código capaces de realizar tareas concretas. Hoy vamos a ver cómo son definir las funciones en Julia y algunas de sus características más interesantes.

Definición de una función básica en Julia

Las funciones en Julia se crean con la palabra clave function seguida del nombre de la función y, entre paréntesis, los parámetros de entrada de la función. En las siguientes líneas se pueden escribir tantas instrucciones como sean necesarias para implementar la tarea de esta. Finalizando la función con la palabra clave return, la cual puede devolver o no variables. Una función básica puede ser la elevar al cuadrado un valor que se puede implementar como:

function potencia(x)
    potencia = x^2
    return potencia
end

La función se ha llamado potencia y así es como se puede llamar en el resto del código. Tiene un único parámetro de entrada que es x, parámetro que se puede usar dentro de la función. En la primera línea de código se ha asignado el resultado de x al cuadrado a una nueva variable potencia y finalmente se ha devuelto el valor. Así al escribir en Julia potencia(3) se obtendrá como resultado 9, pero si se escribe potencia(5) el resultado será 25.

Simplificación de las funciones

Las funciones se pueden simplificar. En este caso no es necesario guardar el resultado en una variable intermedia ya que el valor se puede calcular en la línea de final return. Así es posible escribir esta función de una forma más compacta.

function potencia(x)
    return x^2
end

Aunque aún se puede reducir más, si no se incluye la palabra reservada return la función de Julia devolverá como valor el resultado de la última línea. Por lo que en este caso se puede omitir el return.

function potencia(x)
    x^2
end

Desde mi punto de vista en este caso se puede perder algo de legibilidad ya que puede causar confusión que se devuelve, por lo que prefiero usar return.

Finalmente, las funciones que se escribe en una línea aún se pueden resumir todavía más. Omitiendo la palabra clave function y asignado la operación a esta. Por lo que se puede dejar la función solamente como

potencia(x) = x^2

Devolver nada

En una función de Julia también es posible no devolver nada (el dato nothing), para lo que se puede usar return sin ningún valor. Algo que también sucede si no se ejecuta ninguna línea de código. En la siguiente función si el valor que se pasa es positivo o cero, el resultado es el número, en caso contrario la función devolverá nothing.

function positivo(x)
    if x ≥ 0
        return x
    end
end

Esto es así porque si no se cumple que el parámetro de entrada sea positivo o cero nunca se ejecuta ninguna línea en el código anterior.

Múltiples parámetros de entrada

Las funciones de Julia pueden admitir más de un parámetro que se han de separar por comas. Así para calcular la distancia euclídea entre dos puntos se puede hacer escribir la función

function distancia(x₁, y₁, x₂, y₂)
    δx² = (x₁ - x₂)^2
    δy² = (y₁ - y₂)^2
    δ² = δx² + δy²
    δ = sqrt(δ²)
    return δ
end

Lo primero que se puede apreciar es la ventaja de usar Unicode, ya que los nombres de variables con subíndice y superíndice son perfectamente validas. Por ejemplo, el subíndice de 1 se puede obtener escribiendo \_1 y posteriormente tabulador.

La primera línea de esta función calcula la distancia al cuadrado en eje x, la segunda la distancia al cuadrado en el eje y, la tercera la distancia al cuadrado, la quinta tiene la distancia y finalmente se retorna el valor en la quinta.

Variables opcionales

Una opción interesante son los parámetros opcionales que puede no indicarse en la función. Tomando en estos casos un valor por defecto. Valor que se asigna en la definición de las funciones con operador =. Por ejemplo, la función anterior se puede redefinir para calcular la distancia al origen de ordenadas cuando solamente se indique un par de valores.

function distancia(x₁, y₁, x₂=0, y₂=0)
    δx² = (x₁ - x₂)^2
    δy² = (y₁ - y₂)^2
    δ² = δx² + δy²
    δ = sqrt(δ²)
    return δ
end

Siendo x₂ y y₂ por defecto 0 cuando no se indique nada.

Retornar múltiples valores

Las funciones de Julia pueden devolver más de un valor, para lo que solamente hay que separar estos con comas después de la palabra return. Así para devolver también la distancia en el eje x e y en la función anterior, solamente hay que hacer.

function distancia(x₁, y₁, x₂=0, y₂=0)
    δx² = (x₁ - x₂)^2
    δy² = (y₁ - y₂)^2
    δ² = δx² + δy²
    δ = sqrt(δ²)
    return δ, δx², δy²
end

Parámetros dinámicos

En algunas situaciones puede ser de interés tener parámetros dinámicos, es decir, que se puede indicar tantos parámetros como se desee. Por ejemplo, una función que sume cualquier cantidad de números. Esto se puede hacer con parámetros opcionales que se indica con el nombre del parámetro seguido de .... Los parámetros adicionales aparecerán como una dupla dentro de este parámetro

function valores(x...)
    @show(x)
end

valores(1,2,3)

Este es un código que devuelve una tupla con los tres valores, esto es x = (1,2,3). Aunque es posible indicar parámetros antes, lo que hará que el primer parámetro se pase normalmente y el resto en una tupla.

function valores(x, y...)
    @show(x)
    @show(y)
end

valores(1,2,3)

Lo que asigna al primer parámetro x = 1 y al segundo y = (2,3).

Indicar el tipo de dato

Julia admite que se indique el tipo de dato para una función. Lo que permite sobrecargar las funciones creando una para cada tipo de dato diferente. Así podemos crear una función factorial que solamente funcione con enteros.

function fact(n::Int64)
    if n == 0
        return 1
    else
        return n * fact(n-1)
    end
end

En la función se ha indicado que el parámetro de entrada sea solamente de tipo entero. Para lo que se ha usado :: seguido del tipo de dato. En el caso de que se pase otro tipo la función no será llamada, generando un error. Por ejemplo, si se ejecuta llama a la función con fact(10.5) obtendremos el siguiente error MethodError: no method matching fact(::Float64). Aunque se puede crear una función específicamente para tipos de datos reales.

function fact(n::Float64)
    return "Has pasado un float"
end

Ahora cuando los parámetros sean enteros se ejecutará la primera función y cuando sean reales la segundo. Apareciendo un error en el resto de los casos.

Paso por valor o referencia

Esta siempre es una duda a la hora de trabajar con funciones, lo parámetros se pasan por valor o referencia. Aquí el truco está en saber si los datos son mutables o no. En caso de que no sean mutables los parámetros se pasan por valor, por lo tanto, se puede modificar sin peligro en la función. Por otro lado, cuando son mutables, como un vector, estos se pasan por referencia, por los que si se modifican dentro de la función también se modificarán fuera. Así en el siguiente código la variable x mantiene el valor 2.

function valores(x)
    x = 10
end

x = 2
valores(x)

Pero en este caso no, el vector [1, 2] se transformará en [3, 2].

function valores(x)
    x[1] = 3
end

x = [1, 2]
valores(x)

Macros

Cuando es necesario evaluar código dentro de una función se pueden usar los macros. La sintaxis es similar, solo que en lugar de la palabra function se tiene que usar macro. Además de estos, para llamar a un macro se tiene que usar @ antes del nombre. Por ejemplo, un simple macro con el mensaje "¡Hola Julia!".

macro holaJulia()
    return :( println("¡Hola, Julia!") )
end

@holaJulia

Los macros también admiten parámetros. Para incluirlos en la evaluación es necesario usar $ antes del nombre. Por ejemplo, si queremos cambiar el norme de Julia.

macro holaJulia(nombre)
    return :( println("¡Hola, $($nombre)!") )
end

@holaJulia("Daniel")

Funciones en Julia un gran potencial

Las funciones en Julia ofrecen muchas posibilidades, de las cuales solamente hemos visto una parte, entre las que se encuentra sobrecarga de funciones y tipificado de datos. Lo que permite expresar implementar cualquier los algoritmos que deseemos.