Elixir 中等号的确切含义是什么?
What is the exact meaning of the equal sign in Elixir?
我不明白等号在 Elixir 中的确切含义。
不清楚的是它看起来像是赋值和模式匹配操作的混合体。
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 的核心机制之一,我一直处于锁定状态,觉得我应该回到这个最初的问题。在我完全理解“=”符号到底发生了什么以及逻辑是什么之前,我不会再进一步了。
Where I start to loose my head is why the hell the two last lines are executed without giving the same error. It looks like the equal sign is back to being an assignment operator only.
那是因为左边的变量名与它在 Elixir 中的值不匹配。相反,变量被重新分配给右侧的匹配值。
这与 Erlang 不同,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
等号表示:"try to fit the value of expression on the right to the shape on the left and assigning values accordingly"。所以左边和右边是不同的,你不能切换它们。在右侧,所有变量都必须绑定,因为它是一个表达式。在左侧,即使您使用已经绑定的变量,它们也会被重新分配。
所以首先是在右边你可以有任何你想要的表情:
{: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。相反,您需要使用 pin 运算符:
a = 1
{^a, b} = {2, 3} #match fails
我在这个回答中写了更多关于 pin 运算符的内容:
两种情况:
1) Left hand side
是 placeholder/variable:
- 右边的任何都会分配
示例:
x = 5
y = x (y gets value 5)
x = y (x gets value 5)
2) Left hand side
是 值
- 匹配右手value/variable的值
示例:
5 = x (Error: as x is undefined)
x = 5
5 = x (5 matches with 5)
6 = x (Error: 6 is not matches with 5)
我不明白等号在 Elixir 中的确切含义。 不清楚的是它看起来像是赋值和模式匹配操作的混合体。
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 的核心机制之一,我一直处于锁定状态,觉得我应该回到这个最初的问题。在我完全理解“=”符号到底发生了什么以及逻辑是什么之前,我不会再进一步了。
Where I start to loose my head is why the hell the two last lines are executed without giving the same error. It looks like the equal sign is back to being an assignment operator only.
那是因为左边的变量名与它在 Elixir 中的值不匹配。相反,变量被重新分配给右侧的匹配值。
这与 Erlang 不同,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
等号表示:"try to fit the value of expression on the right to the shape on the left and assigning values accordingly"。所以左边和右边是不同的,你不能切换它们。在右侧,所有变量都必须绑定,因为它是一个表达式。在左侧,即使您使用已经绑定的变量,它们也会被重新分配。
所以首先是在右边你可以有任何你想要的表情:
{: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。相反,您需要使用 pin 运算符:
a = 1
{^a, b} = {2, 3} #match fails
我在这个回答中写了更多关于 pin 运算符的内容:
两种情况:
1) Left hand side
是 placeholder/variable:
- 右边的任何都会分配
示例:
x = 5
y = x (y gets value 5)
x = y (x gets value 5)
2) Left hand side
是 值
- 匹配右手value/variable的值
示例:
5 = x (Error: as x is undefined)
x = 5
5 = x (5 matches with 5)
6 = x (Error: 6 is not matches with 5)