El concepto Basics presentó dos formas de definir una función.
De manera más general, la forma multilínea:
function muladd(x, y, z)
x * y + z
end
La forma de «asignación» o «de una sola línea» para definiciones cortas:
muladd(x, y, z) = x * y + z
En una tercera forma, aún más corta, se puede crear una función breve de un solo uso sin nombre:
julia> map(x -> 2x, 1:3)
4-element Vector{Int64}:
2
4
6
julia> map((x, y) -> x * y, 1:3, 4:6)
3-element Vector{Int64}:
4
10
18
En este caso, x -> 2x es una «función anónima».
Esto equivale a lo que otros lenguajes llaman una «función lambda».
Ten en cuenta que varios argumentos necesitan paréntesis, como en (x, y) -> x * y.
Las funciones anónimas son comunes en el código Julia, sobre todo cuando se combinan con funciones de orden superior como map() y filter().
Hasta ahora en el temario solo hemos visto funciones que tienen un número preciso de argumentos y que exigen que las llamadas a la función los proporcionen todos, en el orden correcto. Esto sería restrictivo e incómodo, así que hay varias otras opciones.
Como muchos lenguajes, Julia permite que las definiciones de funciones proporcionen valores predeterminados para argumentos individuales.
Entonces, la llamada a la función puede proporcionar un valor para ese argumento, u omitirlo y usar el valor predeterminado.
julia> f(x, y=10) = x * y
f (generic function with 2 methods)
julia> f(2, 3)
6
julia> f(2)
20
Todos los argumentos sin valores predeterminados deben ir antes que cualquier argumento con valores predeterminados, lo que significa que f(x=2, y) no sería válido.
Todos los ejemplos hasta ahora usan positional arguments, donde los valores proporcionados en una llamada a la función deben coincidir con el orden de los argumentos correspondientes en la definición de la función.
Como muchos lenguajes, Julia también permite keyword arguments.
Las llamadas a la función deben especificar el nombre del argumento, pero luego se pueden indicar varios argumentos de palabra clave en cualquier orden.
Una característica distintiva de Julia es que los argumentos de palabra clave (si los hay) en la definición de la función deben ir precedidos por un punto y coma ; para separarlos de los argumentos posicionales.
Una llamada a la función puede usar ; o , entre el último argumento posicional y el primer argumento de palabra clave.
julia> b(x; y) = x + y
b (generic function with 1 method)
julia> b(2, y=3)
5
# keyword is required when calling
julia> b(2, 3)
ERROR: MethodError: no method matching b(::Int64, ::Int64)
The function `b` exists, but no method is defined for this combination of argument types.
De manera opcional se pueden especificar valores predeterminados, exactamente igual que para los argumentos posicionales.
Es común terminar con una sintaxis como myarg=myarg dentro de una llamada a la función, cuando se calculó de antemano una variable con el mismo nombre que el parámetro.
En esta situación se permite una sintaxis abreviada:
julia> width = 4.0
4.0
julia> height = √ width
2.0
julia> area(; width, height) = width * height
area (generic function with 1 method)
# repetition
julia> area(; width=width, height=height)
8.0
# shorthand form
julia> area(; width, height)
8.0
Estos son los nombres estándar de un aspecto útil de la sintaxis de Julia, por si te lo preguntabas.
Ambos se refieren al operador ....
El splat se usa en las llamadas a funciones para expandir colecciones en los valores individuales que la función necesita.
Quizás sea más fácil mostrarlo que explicarlo:
julia> fxyz(x, y, z) = x * y * z
fxyz (generic function with 1 method)
julia> xyz = [2, 3, 4]
3-element Vector{Int64}:
2
3
4
# Using the vector directly in a function call is invalid
julia> fxyz(xyz)
ERROR: MethodError: no method matching fxyz(::Vector{Int64})
The function `fxyz` exists, but no method is defined for this combination of argument types.
# splatting converts the vector to 3 numbers, used as positional arguments
julia> fxyz(xyz...)
24
Algunas «llamadas a funciones» están ocultas por el azúcar sintáctico, así que también se puede hacer splat de formas menos obvias.
Por ejemplo, la asignación múltiple usa internamente una función constructora de tuplas:
julia> first, rest... = [1, 2, 3, 4]
4-element Vector{Int64}:
1
2
3
4
julia> first
1
julia> rest
3-element Vector{Int64}:
2
3
4
Los argumentos de palabra clave también se pueden proporcionar con splat, por lo general usando una named tuple.
Un Dict también funciona, pero las claves deben ser símbolos (los string no funcionan aquí).
# function with 3 keyword arguments
julia> fabc(; a, b, c) = a + b + c
fabc (generic function with 1 method)
# named tuple
julia> abc_nt = (a=2, b=3, c=4)
(a = 2, b = 3, c = 4)
# there are no positional arguments, so need to use ; before kw argument
julia> fabc(;abc_nt...)
9
# Dict
julia> abc_dict = Dict(:a=>2, :b=>3, :c=>4)
Dict{Symbol, Int64} with 3 entries:
:a => 2
:b => 3
:c => 4
julia> fabc(;abc_dict...)
9
El slurp se usa en la definición de una función para empaquetar una cantidad arbitraria de valores individuales en una colección.
julia> f_more(i, j, more...) = i + j + sum(more)
f_more (generic function with 1 method)
julia> f_more(1, 3, 5, 7, 9, 11)
36
El nombre del argumento al que se le hace slurp (en este caso more) no es significativo.
El tipo de esta variable lo elige el compilador, pero en el caso de los argumentos posicionales probablemente sea tuple o algo similar.
También se puede hacer slurp de los argumentos de palabra clave, lo que da un Dict (o algo similar).
julia> f_kwslurp(x, y; switches...) = :mult in keys(switches) ? x * y : x + y
f_kwslurp (generic function with 1 method)
julia> f_kwslurp(5, 6; mult=true)
30
julia> f_kwslurp(5, 6)
11
Se puede usar cualquier argumento de palabra clave en la llamada. La definición de la función decide a qué palabras clave responder y cuáles ignorar.