Elixir Language
Funciones
Buscar..
Funciones anónimas
En Elixir, una práctica común es usar funciones anónimas. Crear una función anónima es simple:
iex(1)> my_func = fn x -> x * 2 end
#Function<6.52032458/1 in :erl_eval.expr/5>
La sintaxis general es:
fn args -> output end
Para facilitar la lectura, puede poner paréntesis alrededor de los argumentos:
iex(2)> my_func = fn (x, y) -> x*y end
#Function<12.52032458/2 in :erl_eval.expr/5>
Para invocar una función anónima, llámela por el nombre asignado y agregue .
entre el nombre y los argumentos.
iex(3)>my_func.(7, 5)
35
Es posible declarar funciones anónimas sin argumentos:
iex(4)> my_func2 = fn -> IO.puts "hello there" end
iex(5)> my_func2.()
hello there
:ok
Usando el operador de captura
Para hacer que las funciones anónimas sean más concisas, puede utilizar el operador de captura &
. Por ejemplo, en lugar de:
iex(5)> my_func = fn (x) -> x*x*x end
Puedes escribir:
iex(6)> my_func = &(&1*&1*&1)
Con múltiples parámetros, use el número correspondiente a cada argumento, contando desde 1
:
iex(7)> my_func = fn (x, y) -> x + y end
iex(8)> my_func = &(&1 + &2) # &1 stands for x and &2 stands for y
iex(9)> my_func.(4, 5)
9
Cuerpos múltiples
Una función anónima también puede tener varios cuerpos (como resultado de la coincidencia de patrones ):
my_func = fn
param1 -> do_this
param2 -> do_that
end
Cuando llama a una función con varios cuerpos, Elixir intenta hacer coincidir los parámetros que ha proporcionado con el cuerpo de función adecuado.
Listas de palabras clave como parámetros de función
Utilice listas de palabras clave para los parámetros de estilo 'opciones' que contienen varios pares clave-valor:
def myfunc(arg1, opts \\ []) do
# Function body
end
Podemos llamar a la función anterior así:
iex> myfunc "hello", pizza: true, soda: false
que es equivalente a:
iex> myfunc("hello", [pizza: true, soda: false])
Los valores de argumento están disponibles como opts.pizza
y opts.soda
respectivamente.
Alternativamente, puedes usar los átomos: opts[:pizza]
y opts[:soda]
.
Funciones con nombre y funciones privadas
Funciones nombradas
defmodule Math do
# one way
def add(a, b) do
a + b
end
# another way
def subtract(a, b), do: a - b
end
iex> Math.add(2, 3)
5
:ok
iex> Math.subtract(5, 2)
3
:ok
Funciones privadas
defmodule Math do
def sum(a, b) do
add(a, b)
end
# Private Function
defp add(a, b) do
a + b
end
end
iex> Math.add(2, 3)
** (UndefinedFunctionError) undefined function Math.add/2
Math.add(3, 4)
iex> Math.sum(2, 3)
5
La coincidencia de patrones
Elixir hace coincidir una llamada de función con su cuerpo en función del valor de sus argumentos.
defmodule Math do def factorial(0): do: 1 def factorial(n): do: n * factorial(n - 1) end
Aquí, el factorial de los números positivos coincide con la segunda cláusula, mientras que el factorial(0)
coincide con la primera. (ignorando los números negativos por simplicidad). Elixir intenta hacer coincidir las funciones de arriba a abajo. Si la segunda función se escribe arriba de la primera, obtendremos un resultado inesperado ya que pasa a una recursión sin fin. Porque factorial(0)
coincide con factorial(n)
Cláusulas de guardia
Las cláusulas de guardia nos permiten verificar los argumentos antes de ejecutar la función. Cláusulas de guardia son generalmente preferidos para if
y cond
debido a su facilidad de lectura, y para hacer una cierta técnica de optimización más fácil para el compilador. Se ejecuta la primera definición de función donde coinciden todos los guardias.
Aquí hay un ejemplo de implementación de la función factorial utilizando guardas y la coincidencia de patrones.
defmodule Math do def factorial(0), do: 1 def factorial(n) when n > 0: do: n * factorial(n - 1) end
El primer patrón coincide si (y solo si) el argumento es 0
. Si el argumento no es 0
, la coincidencia del patrón falla y se comprueba la siguiente función a continuación.
Esa segunda definición de función tiene una cláusula de protección: when n > 0
. Esto significa que esta función solo coincide si el argumento n
es mayor que 0
. Después de todo, la función factorial matemática no está definida para enteros negativos.
Si ninguna de las definiciones de funciones (incluidas sus cláusulas de coincidencia de patrones y guardas) coinciden, se generará un FunctionClauseError
. Esto sucede para esta función cuando pasamos un número negativo como argumento, ya que no está definido para números negativos.
Tenga en cuenta que este FunctionClauseError
sí, no es un error. Si devuelve -1
o 0
o algún otro "valor de error", como es común en otros idiomas, ocultará el hecho de que llamó a una función indefinida, ocultando la fuente del error, posiblemente creando un error muy doloroso para un futuro desarrollador.
Parámetros predeterminados
Puede pasar los parámetros predeterminados a cualquier función nombrada usando la sintaxis: param \\ value
:
defmodule Example do def func(p1, p2 \\ 2) do IO.inspect [p1, p2] end end Example.func("a") # => ["a", 2] Example.func("b", 4) # => ["b", 4]
Funciones de captura
Utilice &
para capturar funciones de otros módulos. Puede utilizar las funciones capturadas directamente como parámetros de función o dentro de funciones anónimas.
Enum.map(list, fn(x) -> String.capitalize(x) end)
Puede hacerse más conciso usando &
:
Enum.map(list, &String.capitalize(&1))
Las funciones de captura sin pasar ningún argumento requieren que especifique explícitamente su aridad, por ejemplo, &String.capitalize/1
:
defmodule Bob do
def say(message, f \\ &String.capitalize/1) do
f.(message)
end
end