erlang "pin"操作符的作用是什么?Elixir变量是可变的吗?

e4eetjau  于 2022-12-08  发布在  Erlang
关注(0)|答案(5)|浏览(177)

目前正在尝试理解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本身一样是不可变的呢?
我只是习惯了...

2wnc66cl

2wnc66cl1#

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. In Erlang, you could often see code like this:

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

In Elixir, you could just write:

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:

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. If you are coming from Erlang background, you probably know the f function from shell.

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

In Elixir you just don't have to write the 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

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

is equivalent in Erlang to:

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:

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

oxcyiej72#

elixir中的数据是不可变的,但变量是可重新赋值的。让elixir有点困惑的是你所看到的赋值和模式匹配的组合。
当你使用等号和左边的变量引用时,elixel会首先模式匹配结构,然后执行赋值。当你左边只有一个变量引用时,它会匹配任何结构,因此会像这样赋值:

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

当你左边有一个更复杂的结构时,长生不老药会先把这个结构模式匹配起来,然后再执行赋值。

[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'中的内容相匹配,在酏剂中这是微不足道的:

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

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

icomxhvb

icomxhvb3#

下面是我的简约主义方法:
等号(=)不只是赋值,这里有两种情况:
1.模式匹配。
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
zxlwwiss

zxlwwiss4#

理解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运算符,我们可以将上面的表达式写为

current_password = 'secret-1'

{ new_password, ^current_password } = { 'secret-2', 'secret-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
zvms9eto

zvms9eto5#

模式匹配将左手的值与右侧的值进行匹配。如果匹配并且左侧包含变量,则将右侧的相应值分配给该变量。
脱字符(^)运算符将变量固定在其值上,并防止在使用模式匹配时对此变量进行任何赋值。
参考:https://medium.com/@Julien_Corb/understand-the-pin-operator-in-elixir-a6f534d865a6

相关问题