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


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow