Functional programming 什么是「;pin";运算符,并且Elixir变量是可变的吗?

Functional programming 什么是「;pin";运算符,并且Elixir变量是可变的吗?,functional-programming,erlang,variable-assignment,immutability,elixir,Functional Programming,Erlang,Variable Assignment,Immutability,Elixir,目前正在尝试理解Elixir中的“^”运算符。 网址: 当对重新绑定没有兴趣时,可以使用pin运算符^ 一个变量,但在匹配它的值之前 匹配: 来源- 考虑到这一点,可以将新值附加到符号,如下所示: iex> x = 1 # Outputs "1" iex> x = 2 # Outputs "2" 我还可以做: iex> x = x + 1 # Outputs "3"! 所以我的第一个问题是,;长生不老药变量是可变的吗? 看起来如果是这样的话。。。这在函数式编程语言中难

目前正在尝试理解Elixir中的“^”运算符。 网址:

当对重新绑定没有兴趣时,可以使用pin运算符^ 一个变量,但在匹配它的值之前 匹配:

来源-

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

iex> x = 1  # Outputs "1"
iex> x = 2  # Outputs "2"
我还可以做:

iex> x = x + 1  # Outputs "3"!
所以我的第一个问题是,;长生不老药变量是可变的吗? 看起来如果是这样的话。。。这在函数式编程语言中难道不可能吗

现在我们来看“^”操作符

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)
list |> sort |> filter |> do_something |> another_thing_with
在长生不老药中,你可以写:

list = sort(list)
list = filter(list)
list = do_something_with(list)
list = another_thing_with(list)
这是完全一样的,但它看起来好一点。当然,最好的解决方案是这样写:

SortedList = sort(List),
FilteredList = filter(SortedList),
List3 = do_something_with(FilteredList),
List4 = another_thing_with(List3)
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
。这是为您自动完成的。这意味着,每次在模式匹配的左侧有变量时,都会为其指定新值

如果没有
^
操作符,您将无法在模式匹配的左侧有一个变量,因为它将从右侧获得新值
^
表示不为此变量分配新内容-将其视为文字值

这就是为什么在长生不老药

x = 1
[1, x, 3] = [1, 2, 3]
在Erlang中相当于:

X = 1,
[1, CompletelyNewVariableName, 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”现在等于1
a=[1,2,3,4]#“a”现在等于[1,2,3,4]
a=%{:what=>“ever”}#“a”现在等于%{:what=>“ever”}
当您在左侧有一个更复杂的结构时,elixir将首先匹配结构的模式,然后执行赋值

[1,a,3]=[1,2,3]
#“a”现在等于2,因为结构匹配
[1,a]=[1,2,3]
#***(匹配错误)**因为结构不一致。
#“a”仍然等于它以前的值
如果要与变量的内容进行值匹配,可以使用pin“^”:

a=[1,2]#“a”现在等于[1,2]
%{:key=>^a}=%{:key=>[1,2]}#模式匹配成功,a仍然等于[1,2]
%{:key=>^a}=%{:key=>[3,4]}**(匹配错误)**
这个人为的例子也可以在右边写上“a”,而不带别针:

%{:key=>[1,2]}=%{:key=>a}
现在假设您想将一个变量分配给结构的一部分,但前提是该结构的一部分与存储在“a”中的内容相匹配,在elixir中,这很简单:

a=%{:from=>“greg”}
[message,^a]=[“Hello”,%{:from=>“greg”}]#“message”等于“Hello”
[消息,^a]=[“你好”,%{:from=>“notgreg”}]#**(匹配错误)**
在这些简单的例子中,针型和模式匹配的使用并不是立即就具有超级价值,但随着您学习更多的长生不老药并开始越来越多的模式匹配,它将成为长生不老药提供的表现力的一部分

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

问题: 允许用户在更改密码之前,必须提供新密码和以前的密码

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

让当前_密码为'secret-1';
常量参数={
新密码:“secret-2”,
当前密码:“secret-2”
}
if(当前密码!==参数当前密码){
抛出“匹配错误”
}
由于用户提供的密码与其当前密码不匹配,上述操作将抛出一个
匹配错误

使用长生不老药,我们可以将上述内容写成

current_password = 'secret-1'

{ new_password, ^current_password } = { 'secret-2', 'secret-2'}
上述情况也会引发
匹配错误
异常

说明: 使用pin运算符
^
与现有变量的值进行模式匹配。在上面的Elixir示例中,变量
new\u password
绑定到元组中的第一项(Elixirs数据结构用
{}
表示),而不是重新绑定
当前的\u password
变量,我们根据其现有值进行模式匹配

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

iex(1)>x=1
1.
iex(2)>^x=1#与以前的值1匹配
1.
iex(3)>^x=2#与以前的值不匹配
**(匹配错误)右侧值不匹配: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”来比较而不是分配它呢
    iex(5)> x
    2