Elixir Language
funzioni
Ricerca…
Funzioni anonime
In Elixir, una pratica comune è usare le funzioni anonime. La creazione di una funzione anonima è semplice:
iex(1)> my_func = fn x -> x * 2 end
#Function<6.52032458/1 in :erl_eval.expr/5>
La sintassi generale è:
fn args -> output end
Per migliorare la leggibilità, puoi mettere le parentesi attorno agli argomenti:
iex(2)> my_func = fn (x, y) -> x*y end
#Function<12.52032458/2 in :erl_eval.expr/5>
Per richiamare una funzione anonima, chiamala con il nome assegnato e aggiungi .
tra il nome e gli argomenti.
iex(3)>my_func.(7, 5)
35
È possibile dichiarare funzioni anonime senza argomenti:
iex(4)> my_func2 = fn -> IO.puts "hello there" end
iex(5)> my_func2.()
hello there
:ok
Utilizzando l'operatore di cattura
Per rendere più concise le funzioni anonime è possibile utilizzare l' operatore di cattura &
. Ad esempio, invece di:
iex(5)> my_func = fn (x) -> x*x*x end
Tu puoi scrivere:
iex(6)> my_func = &(&1*&1*&1)
Con più parametri, utilizza il numero corrispondente a ciascun argomento, contando da 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
Corpi multipli
Una funzione anonima può anche avere più corpi (come risultato della corrispondenza del modello ):
my_func = fn
param1 -> do_this
param2 -> do_that
end
Quando si chiama una funzione con più corpi, l'elisir tenta di far corrispondere i parametri forniti con il corpo della funzione appropriata.
Elenchi di parole chiave come parametri di funzione
Utilizza gli elenchi di parole chiave per i parametri di stile "opzioni" che contengono più coppie chiave-valore:
def myfunc(arg1, opts \\ []) do
# Function body
end
Possiamo chiamare la funzione sopra in questo modo:
iex> myfunc "hello", pizza: true, soda: false
che è equivalente a:
iex> myfunc("hello", [pizza: true, soda: false])
I valori degli argomenti sono disponibili rispettivamente come opts.pizza
e opts.soda
.
In alternativa, potresti usare gli atomi: opts[:pizza]
e opts[:soda]
.
Funzioni nominate e funzioni private
Funzioni nominate
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
Funzioni private
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
Pattern Matching
L'elisir corrisponde una chiamata di funzione al suo corpo in base al valore dei suoi argomenti.
defmodule Math do def factorial(0): do: 1 def factorial(n): do: n * factorial(n - 1) end
Qui, fattoriale di numeri positivi corrisponde alla seconda clausola, mentre factorial(0)
corrisponde alla prima. (ignorando i numeri negativi per motivi di semplicità). L'elisir cerca di far corrispondere le funzioni dall'alto verso il basso. Se la seconda funzione è scritta sopra la prima, avremo un risultato inaspettato mentre si procede verso una ricorsione senza fine. Perché factorial(0)
corrisponde a factorial(n)
Clausole di guardia
Le clausole di guardia ci consentono di controllare gli argomenti prima di eseguire la funzione. Le clausole di guardia sono solitamente preferite a if
e cond
causa della loro leggibilità e per rendere più semplice una determinata tecnica di ottimizzazione per il compilatore. La prima definizione di funzione in cui tutte le protezioni corrispondono viene eseguita.
Ecco un esempio di implementazione della funzione fattoriale usando le protezioni e la corrispondenza del modello.
defmodule Math do def factorial(0), do: 1 def factorial(n) when n > 0: do: n * factorial(n - 1) end
Il primo schema corrisponde se (e solo se) l'argomento è 0
. Se l'argomento non è 0
, la corrispondenza del modello fallisce e viene controllata la funzione successiva.
Quella seconda definizione di funzione ha una clausola di guardia: when n > 0
. Ciò significa che questa funzione corrisponde solo se l'argomento n
è maggiore di 0
. Dopo tutto, la funzione fattoriale matematica non è definita per gli interi negativi.
Se nessuna delle definizioni di funzione (incluse la corrispondenza del modello e le clausole di protezione) corrispondono, verrà generato un FunctionClauseError
. Questo accade per questa funzione quando passiamo un numero negativo come argomento, poiché non è definito per i numeri negativi.
Notare che questo FunctionClauseError
stesso non è un errore. Restituire -1
o 0
o qualche altro "valore di errore" come è comune in alcune altre lingue nasconderebbe il fatto che hai chiamato una funzione indefinita, nascondendo la fonte dell'errore, creando probabilmente un enorme bug doloroso per un futuro sviluppatore.
Parametri predefiniti
È possibile passare i parametri predefiniti a qualsiasi funzione denominata utilizzando la sintassi: 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]
Cattura le funzioni
Usa &
per acquisire funzioni da altri moduli. È possibile utilizzare le funzioni acquisite direttamente come parametri di funzione o all'interno di funzioni anonime.
Enum.map(list, fn(x) -> String.capitalize(x) end)
Può essere reso più conciso usando &
:
Enum.map(list, &String.capitalize(&1))
Catturare le funzioni senza passare alcun argomento richiede di specificare esplicitamente la propria appartenenza, ad esempio &String.capitalize/1
:
defmodule Bob do
def say(message, f \\ &String.capitalize/1) do
f.(message)
end
end