Каково точное значение знака равенства в Эликсире?

Я не понимаю, что именно означает знак равенства в Эликсире. Что неясно, так это то, что это выглядит как смесь присваивания и операции сопоставления с образцом.

iex(1)> x=4
4
iex(2)> y=5
5
iex(3)> 3=y
** (MatchError) no match of right hand side value: 5

iex(3)> y=3
3
iex(4)> y=x
4

Я понимаю, что в Elixir оператор равенства означает соответствие левой части знака = правой стороне. Для меня первые две строчки имеют смысл. x и y - несвязанные переменные, поэтому они могут соответствовать чему угодно. Они связаны по мере их совпадения. Следовательно, я понимаю третью строчку. Вы не можете сопоставить 3 с 5.

Я начинаю терять голову, почему, черт возьми, две последние строки выполняются без той же ошибки. Похоже, что знак равенства снова стал оператором присваивания.

Я пытался принять такое поведение как факт без полного понимания и попытался пойти дальше в изучении языка. Но поскольку сопоставление с образцом является одним из основных механизмов Elixir, я постоянно блокируюсь и чувствую, что должен вернуться к этому исходному вопросу. Я не буду идти дальше, пока полностью не пойму, что именно происходит со знаком «=» и какова логика.


person Melicerte    schedule 24.06.2016    source источник


Ответы (3)


Знак равенства означает: «попробуйте подогнать значение выражения справа к форме слева и присвоить значения соответствующим образом». Итак, левая и правая стороны разные, и вы не можете их переключить. Справа должны быть связаны все переменные, потому что это выражение. С левой стороны, даже если вы используете уже привязанные переменные, они будут переназначены.

Итак, во-первых, справа вы можете иметь любое выражение, которое хотите:

{:error, :enoent} = File.open("foo")

но у вас не может быть выражения в левой части:

iex(1)> File.open("foo") = {:error, :enoent}
** (CompileError) iex:1: cannot invoke remote function File.open/1 inside match

В случае

y=3
5=y # y gets evaluated to 3 and then you pattern match 3=5

и это не удается. Но ты можешь сделать

y=3
y=5 # y gets reassigned.

С левой стороны у вас может быть только "shape", который может быть произвольно вложенной структурой данных:

[a, b, %{"a" => {1, c}}] = [1, 2, %{"a" => {1, 2}]
# c is now assigned value of 2

Таким образом, сопоставление с образцом используется либо для разрушения данных, либо для утверждения некоторого условия, например

case File.open("foo") do
  {:ok, contents} -> enjoy_the_file(contents)
  {:error, reason} -> print_error(reason)
end

или если вы хотите утверждать, что в базе данных есть только одна сущность, вместо того, чтобы сначала утверждать, что она существует, а затем что есть только одна, которую вы можете сопоставить с шаблоном:

[entity] = Repo.all(query)

Если вы хотите утверждать, что первое значение в списке равно единице, вы можете сопоставить шаблон:

[1 | rest] = [1, 2, 3]

При сопоставлении с образцом есть несколько ошибок. Например это:

%{} = %{a: "a"}

будет соответствовать, потому что форма на левой стороне - это карта, и вам больше ничего не требуется, поэтому любая карта будет соответствовать. Однако это не будет совпадать:

%{a: "a"} = %{}

потому что фигура слева говорит: «Дайте мне карту с ключом атома :a.

Если вы хотите сопоставить переменную, вы можете написать что-то вроде этого:

a = 1
{a, b} = {2, 3}

но это присвоит a значение 2. Вместо этого вам нужно использовать оператор вывода:

a = 1
{^a, b} = {2, 3} #match fails

В этом ответе я больше писал об операторе вывода: Для чего нужен оператор pin и изменяемы ли переменные Elixir?

person tkowal    schedule 24.06.2016
comment
В подводных камнях также может быть полезно указать, что, хотя %{} = %{a: :b} будет соответствовать, [] =[:a, :b] не будет совпадать. Но это может выходить за рамки вашего ответа. ¯_ (ツ) _ / ¯ - person Cody Poll; 24.06.2016
comment
Спасибо за подробное объяснение. Мне нравится метафора формы и выражения, которая проясняет мне жизнь. Я также нашел этот bordeltabernacle.github.io/ 2015/12/31 /, которые помогли мне продвинуться дальше. - person Melicerte; 26.06.2016
comment
Из вышеупомянутого сообщения, в частности, из этой цитаты Джо Амстронга, создателя Erlang, что знак равенства больше похож на символ = в алгебре (подумайте о x +5 = 10). - person Melicerte; 26.06.2016

Я начинаю терять голову, почему, черт возьми, две последние строки выполняются без той же ошибки. Похоже, что знак равенства снова стал оператором присваивания.

Это потому, что имя переменной в левой части не соответствует ее значению в Elixir. Вместо этого переменной назначается соответствующее значение с правой стороны.

Это отличается от Erlang, где происходит именно то, что вы ожидаете:

1> X = 4.
4
2> Y = 5.
5
3> 3 = Y.
** exception error: no match of right hand side value 5
4> Y = 3.
** exception error: no match of right hand side value 3
5> Y = X.
** exception error: no match of right hand side value 4

Чтобы получить такое же поведение в Elixir, вам нужно использовать оператор «pin» для каждой переменной, которую вы хотите сопоставить по значению с левой стороны:

iex(1)> x = 4
4
iex(2)> y = 5
5
iex(3)> 3 = y
** (MatchError) no match of right hand side value: 5

iex(3)> ^y = 3
** (MatchError) no match of right hand side value: 3

iex(3)> ^y = x
** (MatchError) no match of right hand side value: 4
person Dogbert    schedule 24.06.2016
comment
Таким образом, оператор булавки заставляет знак равенства быть оператором только для совпадения. Без булавки это совпадение и назначение. правильно ? - person Melicerte; 26.06.2016

Два случая:

1) Left hand side - это заполнитель / переменная:

  • Правильное будет назначено

Пример:

x = 5
y = x (y gets value 5)
x = y (x gets value 5)

2) Left hand side - значение

  • Соответствует значению справа / значению переменной

Пример:

5 = x (Error: as x is undefined)
x = 5
5 = x (5 matches with 5)
6 = x (Error: 6 is not matches with 5)
person Dinesh Balasubramanian    schedule 17.10.2016