Поиск…


Анонимные функции

В 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


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow