Elixir Language
функции
Поиск…
Анонимные функции
В Elixir распространенной практикой является использование анонимных функций. Создание анонимной функции прост:
iex(1)> my_func = fn x -> x * 2 end
#Function<6.52032458/1 in :erl_eval.expr/5>
Общий синтаксис:
fn args -> output end
Для удобства чтения вы можете поместить круглые скобки вокруг аргументов:
iex(2)> my_func = fn (x, y) -> x*y end
#Function<12.52032458/2 in :erl_eval.expr/5>
Чтобы вызвать анонимную функцию, вызовите ее по назначенному имени и добавьте .
между именем и аргументами.
iex(3)>my_func.(7, 5)
35
Анонимные функции можно объявлять без аргументов:
iex(4)> my_func2 = fn -> IO.puts "hello there" end
iex(5)> my_func2.()
hello there
:ok
Использование оператора захвата
Чтобы сделать анонимные функции более краткими, вы можете использовать оператор захвата &
. Например, вместо:
iex(5)> my_func = fn (x) -> x*x*x end
Ты можешь написать:
iex(6)> my_func = &(&1*&1*&1)
С помощью нескольких параметров используйте число, соответствующее каждому аргументу, считая от 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
Несколько тел
Анонимная функция также может иметь несколько тел (в результате сопоставления с образцом ):
my_func = fn
param1 -> do_this
param2 -> do_that
end
Когда вы вызываете функцию с несколькими телами, Elixir пытается сопоставить параметры, которые вы предоставили с правильным телом функции.
Списки ключевых слов как функциональные параметры
Используйте списки ключевых слов для параметров параметров «options», которые содержат несколько пар ключ-значение:
def myfunc(arg1, opts \\ []) do
# Function body
end
Мы можем вызвать функцию выше:
iex> myfunc "hello", pizza: true, soda: false
что эквивалентно:
iex> myfunc("hello", [pizza: true, soda: false])
Значения аргументов доступны как opts.pizza
и opts.soda
соответственно.
В качестве альтернативы вы можете использовать атомы: opts[:pizza]
и opts[:soda]
.
Именованные функции и частные функции
Именованные функции
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
Частные функции
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
Соответствие шаблону
Эликсир сопоставляет вызов функции своему телу на основе значения его аргументов.
defmodule Math do def factorial(0): do: 1 def factorial(n): do: n * factorial(n - 1) end
Здесь факториал положительных чисел соответствует второму предложению, а factorial(0)
соответствует первому. (игнорируя отрицательные числа для простоты). Эликсир пытается сопоставить функции сверху донизу. Если вторая функция написана над первой, мы получим неожиданный результат, так как это приведет к бесконечной рекурсии. Поскольку factorial(0)
соответствует factorial(n)
Оговорки о защите
Оговорки Guard позволяют нам проверять аргументы перед выполнением функции. Оговорки о защите обычно предпочтительнее, if
и cond
из-за их удобочитаемости, и для того, чтобы упростить компилятор для определенной методики оптимизации . Первое определение функции, в котором выполняется все защитные меры.
Ниже приведен пример реализации факториальной функции с использованием защиты и сопоставления шаблонов.
defmodule Math do def factorial(0), do: 1 def factorial(n) when n > 0: do: n * factorial(n - 1) end
Первый шаблон соответствует if (и только если) аргумент равен 0
. Если аргумент не равен 0
, совпадение шаблона не выполняется, и проверяется следующая функция ниже.
Это второе определение функции имеет предложение охраны: when n > 0
. Это означает, что эта функция соответствует только аргументу n
больше 0
. В конце концов, математическая факторная функция не определена для отрицательных целых чисел.
Если ни одно определение функции (включая их соответствие шаблону и предложения охраны) не будет FunctionClauseError
будет FunctionClauseError
. Это происходит для этой функции, когда мы передаем отрицательное число в качестве аргумента, так как оно не определено для отрицательных чисел.
Обратите внимание, что этот сам FunctionClauseError
не является ошибкой. Возвращение -1
или 0
или другое «значение ошибки», как это принято на некоторых других языках, скроет тот факт, что вы вызывали неопределенную функцию, скрывая источник ошибки, возможно, создавая огромную болезненную ошибку для будущего разработчика.
Параметры по умолчанию
Вы можете передавать параметры по умолчанию любой именованной функции с помощью синтаксиса: 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]
Функции захвата
Используйте &
для захвата функций из других модулей. Вы можете использовать захваченные функции непосредственно как функциональные параметры или в анонимных функциях.
Enum.map(list, fn(x) -> String.capitalize(x) end)
Можно сделать более сжатым использование &
:
Enum.map(list, &String.capitalize(&1))
Захват функций без передачи каких-либо аргументов требует явного указания его арности, например &String.capitalize/1
:
defmodule Bob do
def say(message, f \\ &String.capitalize/1) do
f.(message)
end
end