[英]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 提供的表达能力的一部分。
这是我的简约方法:
等号 (=) 不仅仅是赋值,这里发生了两件事:
把“=”想象成代数,这表示等式的左边和右边代表相同,所以如果你有 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.