Spec-Zone.ru › Elixir 1.16

Исходный код Анонимные функции

Анонимные функции позволяют нам хранить и передавать исполняемый код как целые числа или строки. Давайте узнаем больше.

Определение анонимных функций

Анонимные функции в 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. Вы можете узнать больше об операторе захвата & в его документации.

Далее давайте пересмотрим некоторые типы данных, которые мы изучали ранее, и углубимся в их работу.

← Предыдущая страница case, cond и if
Следующая страница → Бинарные данные, строки и списки символов

Скачать версию ePub

Создано с помощью ExDoc (v0.32.2) для языка программирования Elixir

© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.16.3/anonymous-functions.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API