В elixir вы можете определять функции с несколькими предложениями, а elixir использует так называемое сопоставление с шаблоном , чтобы определить, какое предложение выполнить.Как вы и подозревали, когда вы определяете несколько предложений функций, вы фактически создаете неявные if-else if
, например, , если аргументы функции, указанные в вызове функции, соответствуют параметрам первого предложения функции, then выполнить первое предложение функции, иначе, если аргументы функции, указанные в вызове функции, соответствуют параметрам второго предложения функции, затем выполнить второе предложение функции и т. Д. .Вот пример:
my.exs:
defmodule My do
def calc(x, 1) do
x * 3
end
def calc(x, 2) do
x - 4
end
def test do
IO.puts calc(5, 1)
IO.puts calc(5, 2)
end
end
(Я обычно не оставляю пустую строку между несколькими предложениями определения функции, чтобы указать, что они все являются одной и той же функцией.)
В iex:
$ iex my.exs
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> My.test
15
1
:ok
iex(2)>
При вызове calc(5, 1)
эликсир переходит к определению calc()
, а эликсир начинается с первого предложения функции и пытается сопоставить аргументы5, 1
против параметров x, 1
.Поскольку параметр x
является переменной, он может соответствовать чему угодно, тогда как параметр 1
может соответствовать только 1
.Следовательно, есть совпадение, и x получает значение 5
.В других языках у вас не может быть значений, таких как 1
, "hello"
, %{a: 1}
или :error
, указанных в качестве параметра функции в определении функции - скорее все параметры функции должны быть переменными.
Аналогично, когда вы вызываете calc(5, 2)
, эликсир переходит к определению calc()
, и эликсир начинается с первого предложения функции и пытается сопоставить аргументы 5, 2
в вызове функции с параметрами x, 1
.Параметр x
может соответствовать чему угодно, но параметр 1
не соответствует аргументу 2
, поэтому elixir переходит к следующему условию функции и пытается сопоставить аргументы 5, 2
с параметрами x, 2
.Это приводит к совпадению, и x получает назначение 5.
В Elixir есть довольно интересные правила сопоставления.Вот пара правил, с которыми вы можете столкнуться в какой-то момент, которые может быть трудно расшифровать:
def func(%{] = x)
будет соответствовать любому аргументу, который является картой - не простопустая карта - и карте будет присвоена переменная x
.То же самое касается структур, например, def func(%Dog{} = x)
будет соответствовать любой структуре Dog.
def func("hello " <> subject)
будет соответствовать любому строковому аргументу, который начинается с "hello "
, а остальная часть строки будетприсваивается переменной subject
.
Elixir также позволяет определять анонимные функции с несколькими предложениями .Если вы измените test()
на:
def test do
func = fn
(:ok, x) -> IO.puts ":ok branch, x = #{x}"
(y, :error) -> IO.puts ":error branch, y = #{y}"
end
func.("hello", :error)
func.(:ok, 10)
end
, то в iex вы увидите:
~/elixir_programs$ iex my.exs
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> My.test()
:error branch, y = hello
:ok branch, x = 10
:ok
iex(2)>
Соответствие работает так же, как и с именованными функциями: elixir просматривает предложения функций по порядкуи пытается сопоставить аргументы в вызове функции с параметрами в предложениях функции.Обратите внимание, что порядок, в котором вы определяете функциональные предложения в вашем коде, может иметь значение.Легко определить функциональное предложение, которое никогда не будет выполнено, например:
func = fn
(x) -> ...
(:ok) -> ...
end
Поскольку параметр x в первом предложении функции будет соответствовать любому аргументу, второе предложение функции никогда не сможет выполняться.К счастью, elixir предупредит вас, если вы сделаете это.
И, поскольку elixir - это функциональный язык, было бы упущением не показывать пример рекурсии.Добавьте следующее определение count()
в модуль My:
def count(0) do
:ok
end
def count(n) when n > 0 do
Process.sleep 1_000 #sleep for 1 second
IO.puts n
count(n-1)
end
Затем в iex:
~/elixir_programs$ iex my.exs
Erlang/OTP 20 [erts-9.3] [source]
[64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe]
[kernel-poll:false] Interactive Elixir (1.6.6) - press Ctrl+C to exit
(type h() ENTER for help)
iex(1)> My.count(10)
10
9
8
7
6
5
4
3
2
1
:ok
iex(2)>
Когда код вызывает count(n-1)
, elixir переходит к первому условию функции count()
, затем пытается сопоставить любое значение аргумента n-1
с параметром 0
в предложении функции.Если совпадений нет, elixir пробует второе предложение функции.В результате второе предложение функции продолжает соответствовать вызову функции count(n-1)
до тех пор, пока все числа, включая 1, не будут выведены, после чего n-1
будет равно 0 в вызове функции, что приведет к вызову функции count(0)
, что в конечном итоге будет соответствоватьпервое функциональное предложение и первое функциональное предложение ничего не выводят и не вызывают сами, поэтому рекурсия заканчивается.
Ключевые вещи, которые нужно изучить в elixir:
- Сопоставление с образцом
- Рекурсия
- Создание других процессов
Удачи!