简体   繁体   English

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

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

Currently trying to understand the "^" operator in Elixir.目前正在尝试了解 Elixir 中的“^”运算符。 From the website:从网站:

The pin operator ^ can be used when there is no interest in rebinding a variable but rather in matching against its value prior to the match:当没有兴趣重新绑定变量而是在匹配之前与其值匹配时,可以使用 pin 运算符 ^ :

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

With this in mind, you can attach a new value to a symbol like so:考虑到这一点,您可以为符号附加一个新值,如下所示:

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

I can also do:我也可以这样做:

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

So my first question is;所以我的第一个问题是; Are Elixir variables mutable? Elixir 变量是可变的吗? It sure looks like if that's the case... Shouldn't that be possible in a functional programming language?看起来确实是这样……这在函数式编程语言中不应该是可能的吗?

So now we come to the "^" operator...所以现在我们来到“^”运算符......

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

I think the effect of "^" is to lock "x" to the last value binded to it.我认为“^”的作用是将“x”锁定到绑定到它的最后一个值。 Is that all there is to it?这就是全部吗? Why not just ensure that all 'matches'/assignments are immutable like Erlang itself?为什么不确保所有“匹配”/分配像 Erlang 本身一样不可变?

I was just getting used to that...我只是习惯了...

The data in Elixir is still immutable, but there are couple of shorthands, that let you type less or don't worry about finding new names. Elixir 中的数据仍然是不可变的,但是有一些速记,可以让你少输入或者不用担心寻找新名称。 In Erlang, you could often see code like this:在 Erlang 中,你经常可以看到这样的代码:

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

In Elixir, you could just write:在 Elixir 中,你可以这样写:

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

This is exactly the same, but it looks a little better.这是完全一样的,但它看起来好一点。 Of course the best solutions would be to write like this:当然,最好的解决方案是这样写:

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

Every time, you assign new thing to list variable, you get new instance:每次,您将新事物分配给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

You just say, that you are no longer interested in the old a and let it point to something else.你只是说,你不再对旧的a感兴趣,让它指向别的东西。 If you are coming from Erlang background, you probably know the f function from shell.如果你有 Erlang 背景,你可能知道 shell 中的f函数。

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

In Elixir you just don't have to write the f .在 Elixir 中,您不必编写f It is done automatically for you.它会自动为您完成。 This means, that every time, you have variable on the left side of the pattern match, you are assigning new value to it.这意味着,每次模式匹配的左侧都有变量时,您都在为其分配新值。

Without the ^ operator, you wouldn't be able to have a variable on the left side of pattern match, because it would get new value from the right side.如果没有^运算符,您将无法在模式匹配的左侧拥有变量,因为它将从右侧获得新值。 ^ means do not assign new things to this variable - treat it as a literal value . ^意味着不要给这个变量分配新的东西——把它当作一个字面值

That is why in Elixir这就是为什么在 Elixir 中

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

is equivalent in Erlang to:在 Erlang 中相当于:

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

and:和:

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

is equivalent to:相当于:

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

which in Erlang is:在 Erlang 中是:

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

Data in elixir is immutable, variables though are re-assignable. Elixir 中的数据是不可变的,但变量是可以重新分配的。 What can make elixir slightly confusing is the combined assignment and pattern matching that you are seeing.使 elixir 有点混乱的是您看到的组合赋值和模式匹配。

When you use the equals sign with a variable reference on the left elixir will first pattern match the structure, and then perform an assignment.当你使用等号和左边的变量引用时,elixir 将首先模式匹配结构,然后执行赋值。 When you have just a sole variable reference on the left, it will match any structure and so will be assigned like so:当左侧只有一个唯一的变量引用时,它将匹配任何结构,因此将像这样分配:

 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"}

When you have a more complex structure on the left elixir will first pattern match the structures, then perform the assignment.当你在左边有更复杂的结构时,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

If you want to value match against the contents of a variable you can use the pin '^':如果要对变量的内容进行值匹配,可以使用引脚“^”:

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)**

This contrived example could also have been written with 'a' on the right hand side and without the pin:这个人为的例子也可以在右手边用“a”写,而不用大头针:

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

Now say you wanted to assign a variable to part of a structure but only if part of that structure matched something stored in 'a', in elixir this is trivial:现在假设您想将一个变量分配给结构的一部分,但前提是该结构的一部分与存储在 'a' 中的内容相匹配,在 elixir 中这是微不足道的:

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

In these simple examples the use of the pin and pattern matching isn't immediately super valuable, but as you learn more elixir and start pattern matching more and more it becomes part of the expressiveness that elixir affords.在这些简单的例子中,pin 和模式匹配的使用并不是立即就非常有价值,但是随着你学习更多的 elixir 并越来越多地开始模式匹配,它成为 elixir 提供的表达能力的一部分。

Here is my minimalistic approach:这是我的简约方法:

The equal symbol (=) is not just assignation, two things happen here:等号 (=) 不仅仅是赋值,这里发生了两件事:

  1. pattern matching.模式匹配。
  2. if the pattern matches, then this leads to an assignment from right to left.如果模式匹配,那么这将导致从右到左的分配。 Otherwise, an error is reported.否则会报错。

Think of "=" as in algebra, this indicates the left and the right side of the equation are representing the same, so if you have x = 1, the only value for x is 1.把“=”想象成代数,这表示等式的左边和右边代表相同,所以如果你有 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

so how can we use 'x' to compare and not to assign it a new value?那么我们如何使用“x”进行比较而不是为其分配新值?

We need to use the pin operator ^:我们需要使用 pin 运算符 ^:

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

we can see that 'x' value is still 2.我们可以看到 'x' 值仍然是 2。

iex(5)> x
2

The best way to understand Elixir's pin operator ^ is with relatable examples.了解 Elixir 的 pin 运算符^的最佳方法是使用相关示例。

Problem: 问题:

Users are allowed to change their passwords before they do, they will have to provide a new password and their previous password.允许用户在更改密码之前更改密码,他们必须提供新密码和以前的密码。

Solution: 解决方案:

In a language like JavaScript, we can write a naive solution like so在像 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" }

The above will throw a Match Error because the user's supplied password does not match their current password以上将抛出Match Error因为用户提供的密码与他们当前的密码不匹配

Using Elixir's pin operator we can write the above as使用 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

The above will also rais a MatchError exception以上也会MatchError异常

Explanation: 解释:

Use the pin operator ^ to pattern match against an existing variable's value.使用 pin 运算符^对现有变量的值进行模式匹配。 In the Elixir's example above, the variable new_password is bound to the first item in the tuple (Elixirs data structure represented with {} ), rather than rebinding the current_password variable, we pattern match against its existing value.在上面的 Elixir 示例中,变量new_password绑定到元组中的第一项(用{}表示的 Elixirs 数据结构),而不是重新绑定current_password变量,我们对其现有值进行模式匹配。

Now this example from Elixir's docs should make sense.现在这个来自 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

Pattern matching matches the value on the left hand side with the value on the right hand side.模式匹配将左侧的值与右侧的值匹配。 if it matches and if the left hand side includes a variable, it assigns the corresponding value from the right hand side to the variable.如果匹配并且左侧包含变量,则将右侧的相应值分配给变量。

Caret(^) operator pins the variable on its value and prevent any assignment to this variable when using pattern matching. Caret(^) 运算符将变量固定在其值上,并在使用模式匹配时防止对该变量进行任何赋值。

Reference: https://medium.com/@Julien_Corb/understand-the-pin-operator-in-elixir-a6f534d865a6参考: 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