Понимание соответствия шаблонов в параметрах функции Elixir - PullRequest
1 голос
/ 26 июня 2019

В книге «Эликсир в действии» один из примеров имеет функцию, которая подрывает мое понимание сопоставления с образцом:

def add_entry(
    %TodoList{entries: entries, auto_id: auto_id} = todo_list,
    entry
  ) do
    entry = Map.put(entry, :id, auto_id)
    new_entries = HashDict.put(entries, auto_id, entry)

    %TodoList{todo_list |
      entries: new_entries,
      auto_id: auto_id + 1
    }
 end

Первый параметр, %TodoList{entries: entries, auto_id: auto_id} = todo_list, объясняет книга «... Более того, вы храните весь экземпляр в todo_list переменной "

. Это сбивает меня с толку, потому что я думал, что переменные связываются слева от оператора сопоставления с образцом '='.Может ли кто-нибудь помочь объяснить, что происходит с первым параметром и как входящие значения можно использовать внутри тела функции?

Ответы [ 3 ]

2 голосов
/ 26 июня 2019

С переменными:

iex(2)> x = %{a: 1, b: 2}   
%{a: 1, b: 2}

iex(3)>  %{a: 1, b: 2} = y
** (CompileError) iex:3: undefined function y/0

С переменными параметров функции:

defmodule A do

  def go1(z = %{a: a}) do
    IO.inspect z
    IO.puts a
  end

  def go2(%{a: a} = z) do
    IO.inspect z
    IO.puts a
  end

end

В iex:

iex(4)> c "a.ex"  
warning: redefining module A (current version defined in memory)
  a.ex:1
[A] 

iex(5)> map = %{a: 1, b: 2} 
%{a: 1, b: 2}

iex(6)> A.go1(map)
%{a: 1, b: 2}
1
:ok

iex(7)> A.go2(map)
%{a: 1, b: 2}
1
:ok

Аргументы функции соответствуют шаблону переменным параметров функции. И в функции эликсира параметры могут быть постоянными, например, 1 или атом, карты, кортежи и т. Д., А не просто переменная, такая как x, y или z. Вот как работает go1():

    A.go1(%{a: 1 b: 2})
          |-----+----|
                |
                | %{a: a} = %{a: 1 b: 2} 
                V
def go1(z = %{a: a}) do

«Переменная параметра» равна %{a: a}, и она сопоставляется с аргументом функции %{a: 1 b: 2}, который связывает a с 1. Затем вы можете подумать, что вы получите совпадение с шаблоном: z = %{a: 1}, однако совпадение с шаблоном %{a: a} = %{a: 1 b: 2} фактически возвращает правую часть:

iex(10)> %{a: a} = %{a: 1, b: 2}    
%{a: 1, b: 2}

Таким образом, вы получите соответствие шаблону: z = %{a: 1, b: 2}. Вот еще одна демонстрация этого:

iex(13)> z = %{a: a} = %{a: 1, b: 2}
%{a: 1, b: 2}

iex(14)> a
1

iex(15)> z
%{a: 1, b: 2}

Вот как go2() работает:

      A.go1(%{a: 1 b: 2})
            |-----+----|
                  |
                  | z = %{a: 1, b: 2}
                  V
def go2(%{a: a} = z)

z - переменная параметра, и она сопоставляется с аргументом функции %{a: 1 b: 2}. Матч z = %{a: 1 b: 2} возвращает правую часть:

iex(10)> z = %{a: 1, b: 2}
%{a: 1, b: 2}

Итак, затем вы получите соответствие шаблону: %{a: a} = %{a: 1, b: 2}, который связывает a с 1.

Следовательно, все соответствует: при каждом сопоставлении с шаблоном шаблон, содержащий переменные, находится слева от =, а значения - с правой стороны. Если вы ищете правило, это: в списке параметров для определения функции, справа от знака = находится «переменная параметра». Слева - шаблон, который будет сопоставлен после сопоставления «переменной параметра» с аргументом функции (или, как вы сказали бы в других языках: «после назначения аргумента функции переменной параметра»).

2 голосов
/ 26 июня 2019

Я думал, что переменные связываются слева от оператора сопоставления с образцом '='

Это верно, в этом случае переменные entries и auto_id связаны.Правая часть todo_list связана с аргументом функции.

Это похоже на следующее:

iex(1)> foobar = %{foo: "foo", bar: "bar"}
%{bar: "bar", foo: "foo"}
iex(2)> %{foo: matched} = foobar
%{bar: "bar", foo: "foo"}
iex(3)> matched
"foo"

Единственное отличие при выполнении этого в сигнатуре функции заключается в том, что первый шаг определения того, что становится правой стороной, обрабатывается автоматически.

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

def do_something_with_foo(%{foo: matched} = original)

Что объясняется выше, где в теле функции доступны как matched, так и original.Если вы только заботитесь о согласованных значениях, вы можете опустить правую часть:

def do_something_with_foo(%{foo: matched})

В этом случае будет доступно только согласованное значение matched.Совпадение все еще происходит, но структура данных, переданная в качестве первого аргумента функции, которая неявно используется в качестве правой части, как если бы вы использовали =, не связана с переменной.

0 голосов
/ 08 июля 2019

Когда вы указываете %{map_thing: stuff} = map_var, вы говорите, что я ожидаю, что эта переменная будет содержать переменную map_thing, и я хочу, чтобы вы поместили все содержимое этой карты в переменную map_var.Вы можете использовать это, чтобы явно указать необходимые ключи на этой карте, а также захватить все дополнительные и "привязать" их к вашему map_var

. Вы можете делать с этим все возможные полезные вещи, такие как создание сортировкиохранника псевдо в случае

case some_var do
%MyStruct{} = struct_var ->
 # we are saying we expect this var to be of the `MyStruct` variety.
other_case ->
  do_something_else()
end
...