了解Elixir函数参数中的模式匹配

时间:2019-06-26 00:05:58

标签: pattern-matching elixir

在“ Elixir in Action”一书中,其中一个示例具有使我对模式匹配的理解加深的功能:

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 个答案:

答案 0 :(得分:2)

  

我认为变量绑定在'='模式匹配运算符的左侧

是正确的,在这种情况下,entriesauto_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)

上面已说明,其中matchedoriginal在功能主体中均可用。如果仅 在乎匹配值,则可以省略右侧:

def do_something_with_foo(%{foo: matched})

在这种情况下,只有匹配的值matched可用。匹配仍然发生,但是作为第一个参数传递给隐式用作右侧函数的数据结构,就像您曾经使用过=一样,没有绑定到变量。

答案 1 :(得分:2)

带有变量:

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

功能参数与功能参数变量进行模式匹配。并且,在e剂函数中,参数可以是常数,例如。 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 :(得分:0)

当您指定<div> <input id="postall" type="button" value="POST Users" /> <table> <thead> <tr> <th>Id</th> <th>Name</th> <th>Street</th> <th>House Number</th> </tr> </thead> <tbody id="userTablePost"></tbody> </table> 时 您是说我希望这个var将包含变量function renderUsers(users) { const tbody = document.getElementById("userTablePost"); users.forEach(user => { let row = tbody.insertRow(); let cell = row.insertCell(); cell.textContent = user.id; cell = row.insertCell(); cell.textContent = user.name; cell = row.insertCell(); cell.textContent = user.street; cell = row.insertCell(); cell.textContent = user.house_number; }); } ,并且我希望您将此映射的全部内容放入变量%{map_thing: stuff} = map_var中。您可以使用它在该映射中显式指定必需的键,还可以获取所有可选键并将它们“绑定”到您的map_thing

您可以用这种方法做各种有用的事情,例如创建一种伪装的警卫队

map_var