"pin" 运算符是做什么用的,Elixir 变量是可变的吗?
What is the "pin" operator for, and are Elixir variables mutable?
目前正在尝试了解 Elixir 中的“^”运算符。
来自网站:
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:
来源 - 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 本身那样确保所有 'matches'/赋值都是不可变的呢?
我刚刚习惯了...
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 提供的表现力的一部分。
The best way to understand Elixir's pin operator ^
is with relatable examples.
问题:
允许用户在更改密码之前更改密码,他们必须提供新密码和之前的密码。
解决方案:
在像 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 operator 我们可以将上面的代码写成
current_password = 'secret-1'
{ new_password, ^current_password } = { 'secret-2', 'secret-2'}
以上也会引发 MatchError
异常
解释:
使用 pin 运算符 ^
对现有变量的值进行模式匹配。在上面的 Elixir 示例中,变量 new_password
绑定到元组中的第一项(用 {}
表示的 Elixir 数据结构),而不是重新绑定 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
这是我的简约方法:
等号 (=) 不仅仅是赋值,这里发生了两件事:
- 模式匹配。
- 如果模式匹配,则这会导致从右到左的赋值。否则报错。
想想代数中的“=”,这表示等式的左边和右边表示相同,所以如果 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
模式匹配将左侧的值与右侧的值相匹配。如果匹配且左侧包含变量,则将右侧的相应值分配给变量。
Caret(^) 运算符将变量固定在其值上,并防止在使用模式匹配时对此变量进行任何赋值。
参考:https://medium.com/@Julien_Corb/understand-the-pin-operator-in-elixir-a6f534d865a6
目前正在尝试了解 Elixir 中的“^”运算符。 来自网站:
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:
来源 - 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 本身那样确保所有 'matches'/赋值都是不可变的呢?
我刚刚习惯了...
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 提供的表现力的一部分。
问题:The best way to understand Elixir's pin operator
^
is with relatable examples.
允许用户在更改密码之前更改密码,他们必须提供新密码和之前的密码。
解决方案:在像 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 operator 我们可以将上面的代码写成
current_password = 'secret-1'
{ new_password, ^current_password } = { 'secret-2', 'secret-2'}
以上也会引发 MatchError
异常
使用 pin 运算符 ^
对现有变量的值进行模式匹配。在上面的 Elixir 示例中,变量 new_password
绑定到元组中的第一项(用 {}
表示的 Elixir 数据结构),而不是重新绑定 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
这是我的简约方法:
等号 (=) 不仅仅是赋值,这里发生了两件事:
- 模式匹配。
- 如果模式匹配,则这会导致从右到左的赋值。否则报错。
想想代数中的“=”,这表示等式的左边和右边表示相同,所以如果 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
模式匹配将左侧的值与右侧的值相匹配。如果匹配且左侧包含变量,则将右侧的相应值分配给变量。
Caret(^) 运算符将变量固定在其值上,并防止在使用模式匹配时对此变量进行任何赋值。
参考:https://medium.com/@Julien_Corb/understand-the-pin-operator-in-elixir-a6f534d865a6