Исходный код Анонимные функции
Анонимные функции позволяют нам хранить и передавать исполняемый код как целые числа или строки. Давайте узнаем больше.
Определение анонимных функций
Анонимные функции в Elixir ограничены ключевыми словами fn и end.
iex> add = fn a, b -> a + b end #Function<12.71889879/2 in :erl_eval.expr/5> iex> add.(1, 2) 3 iex> is_function(add) true
В приведенном выше примере мы определили анонимную функцию, которая принимает два аргумента, a и b, и возвращает результат a + b. Аргументы всегда находятся слева от ->, а код, который нужно выполнить, справа. Анонимная функция хранится в переменной add.
Мы можем вызвать анонимные функции, передав аргументы. Обратите внимание, что точка (.) между переменной и скобками необходима для вызова анонимной функции. Точка позволяет четко различать вызов анонимной функции, хранящейся в переменной add, от вызова именованной функции add/2. Например, если у вас есть анонимная функция, хранящаяся в переменной is_atom, то нет неоднозначности между is_atom.(:foo) и is_atom(:foo). Если бы оба использовали одинаковый синтаксис is_atom(:foo), единственный способ узнать фактическое поведение is_atom(:foo) состоял бы в сканировании всего кода до этого момента в поисках возможного определения переменной is_atom. Такое сканирование снижает поддерживаемость, поскольку разработчикам необходимо отслеживать дополнительный контекст при чтении и написании кода.
Анонимные функции в Elixir также определяются по количеству аргументов, которые они принимают. Мы можем проверить, является ли функция той или иной арности, используя is_function/2:
# check if add is a function that expects exactly 2 arguments iex> is_function(add, 2) true # check if add is a function that expects exactly 1 argument iex> is_function(add, 1) false
Замыкания
Анонимные функции также могут получать доступ к переменным, которые находятся в области видимости при определении функции. Это обычно называется замыканиями, поскольку они закрывают область видимости. Давайте определим новую анонимную функцию, использующую ранее определенную анонимную функцию add.
iex> double = fn a -> add.(a, a) end #Function<6.71889879/1 in :erl_eval.expr/5> iex> double.(2) 4
Переменная, присвоенная внутри функции, не влияет на окружающую среду:
iex> x = 42 42 iex> (fn -> x = 0 end).() 0 iex> x 42
Разделы и условия
Подобно case/2, мы также можем выполнять сопоставление с образцом по аргументам анонимных функций, а также определять несколько разделов и условий:
iex> f = fn ...> x, y when x > 0 -> x + y ...> x, y -> x * y ...> end #Function<12.71889879/2 in :erl_eval.expr/5> iex> f.(1, 3) 4 iex> f.(-1, 3) -3
Количество аргументов в каждом разделе анонимной функции должно быть одинаковым, в противном случае будет выведено сообщение об ошибке.
iex> f2 = fn ...> x, y when x > 0 -> x + y ...> x, y, z -> x * y + z ...> end ** (CompileError) iex:1: cannot mix clauses with different arities in anonymous functions
Оператор захвата
На протяжении этого руководства мы использовали запись name/arity для обозначения функций. Оказывается, эта запись может фактически использоваться для захвата существующей функции в тип данных, который мы можем передавать, подобно тому, как ведут себя анонимные функции.
iex> fun = &is_atom/1 &:erlang.is_atom/1 iex> is_function(fun) true iex> fun.(:hello) true iex> fun.(123) false
Как видите, после захвата функции мы можем передать её в качестве аргумента или вызвать её, используя обозначение анонимной функции. Возвращаемое значение выше также указывает, что мы можем захватывать функции, определенные в модулях:
iex> fun = &String.length/1
&String.length/1
iex> fun.("hello")
5
Вы также можете захватывать операторы:
iex> add = &+/2 &:erlang.+/2 iex> add.(1, 2) 3
Синтаксис захвата также может использоваться как сокращение для создания функций. Это удобно, когда вы хотите создать функции, которые в основном оборачивают существующие функции или операторы:
iex> fun = &(&1 + 1)
#Function<6.71889879/1 in :erl_eval.expr/5>
iex> fun.(1)
2
iex> fun2 = &"Good #{&1}"
#Function<6.127694169/1 in :erl_eval.expr/5>
iex> fun2.("morning")
"Good morning"
&1 представляет собой первый аргумент, переданный в функцию. &(&1 + 1) выше точно такое же, как fn x -> x + 1 end. Вы можете узнать больше об операторе захвата & в его документации.
Далее давайте пересмотрим некоторые типы данных, которые мы изучали ранее, и углубимся в их работу.
© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.16.3/anonymous-functions.html