繁体   English   中英

“pin”运算符是什么,Elixir 变量是否可变?

[英]What is the "pin" operator for, and are Elixir variables mutable?

目前正在尝试了解 Elixir 中的“^”运算符。 从网站:

当没有兴趣重新绑定变量而是在匹配之前与其值匹配时,可以使用 pin 运算符 ^ :

来源 - http://elixir-lang.org/getting_started/4.html

考虑到这一点,您可以为符号附加一个新值,如下所示:

iex> x = 1  # Outputs "1"
iex> x = 2  # Outputs "2"

我也可以这样做:

iex> x = x + 1  # Outputs "3"!

所以我的第一个问题是; Elixir 变量是可变的吗? 看起来确实是这样……这在函数式编程语言中不应该是可能的吗?

所以现在我们来到“^”运算符......

iex> x = 1  # Outputs "1"
iex> x = 2  # Outputs "2"
iex> x = 1  # Outputs "1"
iex> ^x = 2 # "MatchError"
iex> ^x = 1  # Outputs "1"

我认为“^”的作用是将“x”锁定到绑定到它的最后一个值。 这就是全部吗? 为什么不确保所有“匹配”/分配像 Erlang 本身一样不可变?

我只是习惯了...

Elixir 中的数据仍然是不可变的,但是有一些速记,可以让你少输入或者不用担心寻找新名称。 在 Erlang 中,你经常可以看到这样的代码:

SortedList = sort(List),
FilteredList = filter(SortedList),
List3 = do_something_with(FilteredList),
List4 = another_thing_with(List3)

在 Elixir 中,你可以这样写:

list = sort(list)
list = filter(list)
list = do_something_with(list)
list = another_thing_with(list)

这是完全一样的,但它看起来好一点。 当然,最好的解决方案是这样写:

list |> sort |> filter |> do_something |> another_thing_with

每次,您将新事物分配给list变量,您都会获得新实例:

iex(1)> a = 1
1
iex(2)> b = [a, 2]
[1, 2]
iex(3)> a = 2
2
iex(4)> b
[1, 2] # first a did not change, it is immutable, currently a just points to something else

你只是说,你不再对旧的a感兴趣,让它指向别的东西。 如果你有 Erlang 背景,你可能知道 shell 中的f函数。

A = 1.
f(A).
A = 2.

在 Elixir 中,您不必编写f 它会自动为您完成。 这意味着,每次模式匹配的左侧都有变量时,您都在为其分配新值。

如果没有^运算符,您将无法在模式匹配的左侧拥有变量,因为它将从右侧获得新值。 ^意味着不要给这个变量分配新的东西——把它当作一个字面值

这就是为什么在 Elixir 中

x = 1
[1, x, 3] = [1, 2, 3]

在 Erlang 中相当于:

X = 1,
[1, CompletelyNewVariableName, 3] = [1, 2, 3]

和:

x = 1
[1, ^x, 3] = [1, 2, 3]

相当于:

x = 1
[1, 1, 3] = [1, 2, 3]

在 Erlang 中是:

X = 1,
[1, X, 3] = [1, 2, 3]

Elixir 中的数据是不可变的,但变量是可以重新分配的。 使 elixir 有点混乱的是您看到的组合赋值和模式匹配。

当你使用等号和左边的变量引用时,elixir 将首先模式匹配结构,然后执行赋值。 当左侧只有一个唯一的变量引用时,它将匹配任何结构,因此将像这样分配:

 a = 1 # 'a' now equals 1
 a = [1,2,3,4] # 'a' now equals [1,2,3,4]
 a = %{:what => "ever"} # 'a' now equals %{:what => "ever"}

当你在左边有更复杂的结构时,elixir 将首先模式匹配结构,然后执行赋值。

[1, a, 3] = [1,2,3] 
# 'a' now equals 2 because the structures match
[1, a] = [1,2,3] 
# **(MatchError)** because the structures are incongruent. 
# 'a' still equals it's previous value

如果要对变量的内容进行值匹配,可以使用引脚“^”:

a = [1,2] # 'a' now equals [1,2]
%{:key => ^a} = %{:key => [1,2]} # pattern match successful, a still equals [1,2]
%{:key => ^a} = %{:key => [3,4]} # **(MatchError)**

这个人为的例子也可以在右手边用“a”写,而不用大头针:

%{:key => [1,2]} = %{:key => a}

现在假设您想将一个变量分配给结构的一部分,但前提是该结构的一部分与存储在 'a' 中的内容相匹配,在 elixir 中这是微不足道的:

a = %{:from => "greg"}
[message, ^a] = ["Hello", %{:from => "greg"}] # 'message' equals "Hello"
[message, ^a] = ["Hello", %{:from => "notgreg"}] # **(MatchError)**

在这些简单的例子中,pin 和模式匹配的使用并不是立即就非常有价值,但是随着你学习更多的 elixir 并越来越多地开始模式匹配,它成为 elixir 提供的表达能力的一部分。

这是我的简约方法:

等号 (=) 不仅仅是赋值,这里发生了两件事:

  1. 模式匹配。
  2. 如果模式匹配,那么这将导致从右到左的分配。 否则会报错。

把“=”想象成代数,这表示等式的左边和右边代表相同,所以如果你有 x = 1,x 的唯一值是 1。

iex(1)> x = 1 # 'x' matches 1
1
iex(2)> x # inspecting the value of 'x' we get 1, like in other languages
1
iex(3)> x = 2 # 'x' matches 2
2
iex(4)> x # now 'x' is 2
2

那么我们如何使用“x”进行比较而不是为其分配新值?

我们需要使用 pin 运算符 ^:

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

我们可以看到 'x' 值仍然是 2。

iex(5)> x
2

了解 Elixir 的 pin 运算符^的最佳方法是使用相关示例。

问题:

允许用户在更改密码之前更改密码,他们必须提供新密码和以前的密码。

解决方案:

在像 JavaScript 这样的语言中,我们可以像这样编写一个简单的解决方案

let current_password = 'secret-1'; const params = { new_password: 'secret-2', current_password: 'secret-2' } if (current_password !== params.current_password) { throw "Match Error" }

以上将抛出Match Error因为用户提供的密码与他们当前的密码不匹配

使用 Elixir 的pin 运算符,我们可以将上面的内容写为

iex(1)> x = 1
1
iex(2)> ^x = 1 # Matches previous value 1
1
iex(3)> ^x = 2 # Does not match previous value 
** (MatchError) no match of right hand side value: 2

以上也会MatchError异常

解释:

使用 pin 运算符^对现有变量的值进行模式匹配。 在上面的 Elixir 示例中,变量new_password绑定到元组中的第一项(用{}表示的 Elixirs 数据结构),而不是重新绑定current_password变量,我们对其现有值进行模式匹配。

现在这个来自 Elixir 文档的例子应该是有意义的。

 iex(1)> x = 1 1 iex(2)> ^x = 1 # Matches previous value 1 1 iex(3)> ^x = 2 # Does not match previous value ** (MatchError) no match of right hand side value: 2

模式匹配将左侧的值与右侧的值匹配。 如果匹配并且左侧包含变量,则将右侧的相应值分配给变量。

Caret(^) 运算符将变量固定在其值上,并在使用模式匹配时防止对该变量进行任何赋值。

参考: https : //medium.com/@Julien_Corb/understand-the-pin-operator-in-elixir-a6f534d865a6

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM