Elixir Language
Funkcje
Szukaj…
Funkcje anonimowe
W Elixir powszechną praktyką jest używanie anonimowych funkcji. Tworzenie anonimowej funkcji jest proste:
iex(1)> my_func = fn x -> x * 2 end
#Function<6.52032458/1 in :erl_eval.expr/5>
Ogólna składnia to:
fn args -> output end
Aby zapewnić czytelność, możesz umieścić nawiasy wokół argumentów:
iex(2)> my_func = fn (x, y) -> x*y end
#Function<12.52032458/2 in :erl_eval.expr/5>
Aby wywołać funkcję anonimową, wywołaj ją według przypisanej nazwy i dodaj .
między nazwą a argumentami.
iex(3)>my_func.(7, 5)
35
Możliwe jest deklarowanie funkcji anonimowych bez argumentów:
iex(4)> my_func2 = fn -> IO.puts "hello there" end
iex(5)> my_func2.()
hello there
:ok
Korzystanie z operatora przechwytywania
Aby bardziej zwięzłe funkcje anonimowe można było użyć operatora przechwytywania &
. Na przykład zamiast:
iex(5)> my_func = fn (x) -> x*x*x end
Możesz pisać:
iex(6)> my_func = &(&1*&1*&1)
W przypadku wielu parametrów użyj liczby odpowiadającej każdemu argumentowi, licząc od 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
Wiele ciał
Funkcja anonimowa może również mieć wiele obiektów (w wyniku dopasowania wzorca ):
my_func = fn
param1 -> do_this
param2 -> do_that
end
Kiedy wywołujesz funkcję z wieloma ciałami, Elixir próbuje dopasować parametry, które podałeś z odpowiednim ciałem funkcji.
Listy słów kluczowych jako parametry funkcji
Użyj list słów kluczowych dla parametrów w stylu „opcji”, które zawierają wiele par klucz-wartość:
def myfunc(arg1, opts \\ []) do
# Function body
end
Możemy wywołać powyższą funkcję w następujący sposób:
iex> myfunc "hello", pizza: true, soda: false
co jest równoważne z:
iex> myfunc("hello", [pizza: true, soda: false])
Wartości argumentów są dostępne odpowiednio jako opts.pizza
i opts.soda
.
Alternatywnie możesz użyć atomów: opts[:pizza]
i opts[:soda]
.
Funkcje nazwane i funkcje prywatne
Nazwane funkcje
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
Funkcje prywatne
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
Dopasowywanie wzorów
Eliksir dopasowuje wywołanie funkcji do swojego ciała na podstawie wartości jego argumentów.
defmodule Math do def factorial(0): do: 1 def factorial(n): do: n * factorial(n - 1) end
Tutaj silnia liczb dodatnich odpowiada drugiej klauzuli, podczas gdy factorial(0)
odpowiada pierwszej. (ignorując liczby ujemne dla uproszczenia). Elixir próbuje dopasować funkcje od góry do dołu. Jeśli druga funkcja zostanie zapisana powyżej pierwszej, uzyskamy nieoczekiwany wynik, gdy dojdzie do niekończącej się rekurencji. Ponieważ factorial(0)
pasuje do factorial(n)
Klauzule ochronne
Klauzule ochronne pozwalają nam sprawdzić argumenty przed wykonaniem funkcji. Klauzule straży są zwykle korzystne if
i cond
ze względu na czytelność i zrobić pewną technikę optymalizacji łatwiej kompilator. Pierwsza definicja funkcji, w której wszystkie strażniki są wykonywane.
Oto przykładowa implementacja funkcji silniowej z wykorzystaniem osłon i dopasowywania wzorców.
defmodule Math do def factorial(0), do: 1 def factorial(n) when n > 0: do: n * factorial(n - 1) end
Pierwszy wzorzec pasuje, jeśli (i tylko wtedy) argumentem jest 0
. Jeśli argument nie jest równy 0
, dopasowanie wzorca kończy się niepowodzeniem i sprawdzana jest następna funkcja poniżej.
Ta druga definicja funkcji ma klauzulę ochronną: when n > 0
. Oznacza to, że ta funkcja pasuje tylko wtedy, gdy argument n
jest większy niż 0
. W końcu matematyczna funkcja czynnikowa nie jest zdefiniowana dla ujemnych liczb całkowitych.
Jeśli żadna definicja funkcji (w tym ich dopasowanie do wzorca i klauzule ochronne) nie jest zgodna, wywoływany jest błąd FunctionClauseError
. Dzieje się tak w przypadku tej funkcji, gdy jako argument przekazujemy liczbę ujemną, ponieważ nie jest ona zdefiniowana dla liczb ujemnych.
Zauważ, że sam FunctionClauseError
nie jest błędem. Zwrócenie -1
lub 0
lub innej „wartości błędu”, jak to często bywa w niektórych innych językach, ukryłoby fakt, że wywołałeś niezdefiniowaną funkcję, ukrywając źródło błędu, prawdopodobnie tworząc ogromny bolesny błąd dla przyszłego programisty.
Parametry domyślne
Możesz przekazać parametry domyślne do dowolnej nazwanej funkcji, używając składni: 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]
Funkcje przechwytywania
Użyj &
do przechwytywania funkcji z innych modułów. Możesz użyć przechwyconych funkcji bezpośrednio jako parametrów funkcji lub w ramach funkcji anonimowych.
Enum.map(list, fn(x) -> String.capitalize(x) end)
Może być bardziej zwięzły przy użyciu &
:
Enum.map(list, &String.capitalize(&1))
Przechwytywanie funkcji bez przekazywania żadnych argumentów wymaga jawnego określenia jego arity, np. &String.capitalize/1
:
defmodule Bob do
def say(message, f \\ &String.capitalize/1) do
f.(message)
end
end