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

Tho*_*mas 6 elixir pattern-matching

在《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
Run Code Online (Sandbox Code Playgroud)

第一个参数,%TodoList{entries: entries, auto_id: auto_id} = todo_list本书解释说“......此外,你将整个实例保存在一个todo_list变量中”

这让我很困惑,因为我认为变量绑定在“=”模式匹配运算符的左侧。有人可以帮助解释第一个参数发生了什么以及如何在函数体内使用传入值吗?

7st*_*tud 5

带变量:

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
Run Code Online (Sandbox Code Playgroud)

对于函数参数变量:

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
Run Code Online (Sandbox Code Playgroud)

在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
Run Code Online (Sandbox Code Playgroud)

函数参数与函数参数变量进行模式匹配。而且,在 Elixir 函数中,参数可以是常量,例如 1 或原子、映射、元组等,而不仅仅是变量,如 x、y 或 z。工作原理如下go1()

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

“参数变量”是,它与绑定到 的函数%{a: a}参数 相匹配。然后,您可能认为您得到了模式 match: ,但是,模式匹配实际上返回右侧:%{a: 1 b: 2}a1z = %{a: 1}%{a: a} = %{a: 1 b: 2}

iex(10)> %{a: a} = %{a: 1, b: 2}    
%{a: 1, b: 2}
Run Code Online (Sandbox Code Playgroud)

因此,您会得到模式匹配: 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}
Run Code Online (Sandbox Code Playgroud)

工作原理如下go2()

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

z是参数变量,它与函数参数匹配%{a: 1 b: 2}。匹配z = %{a: 1 b: 2}返回右侧:

iex(10)> z = %{a: 1, b: 2}
%{a: 1, b: 2}
Run Code Online (Sandbox Code Playgroud)

因此,接下来您将获得模式 match: %{a: a} = %{a: 1, b: 2},它绑定a1.

因此,一切都是一致的:对于每个模式匹配,包含变量的模式位于 的左侧=,值位于右侧。如果你正在寻找一个规则,那就是:在函数定义的参数列表中,符号右侧的东西=是“参数变量”。 左边的东西是一个模式,在“参数变量”与函数参数匹配之后(或者正如您在其他语言中所说的:“在函数参数分配给参数变量之后”),它将被匹配。


Ada*_*hip 4

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

这是正确的,在这种情况下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"
Run Code Online (Sandbox Code Playgroud)

在函数签名中执行此操作的唯一区别是,定义右侧内容的第一步是自动处理的。

正如书中所说,您可以像这样定义函数签名:

def do_something_with_foo(%{foo: matched} = original)
Run Code Online (Sandbox Code Playgroud)

上面已对此进行了解释,其中 和matchedoriginal在函数体中可用。如果您关心匹配的值,则可以省略右侧:

def do_something_with_foo(%{foo: matched})
Run Code Online (Sandbox Code Playgroud)

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