À quoi sert l'opérateur " pin " et les variables Elixir sont-elles mutables?

Essaie actuellement de comprendre l'opérateur " ^ " dans Elixir. À partir du site web:

L'opérateur pin ^ peut être utilisé quand il n'y a aucun intérêt à rebinding une variable mais plutôt en correspondance avec sa valeur avant le match:

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

Dans cet esprit, vous pouvez attacher une nouvelle valeur à un symbole comme ceci:

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

Je peux aussi faire:

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

Donc, ma première question est: etes - Variables Elixir mutable? C'est sûr qu'il dirait si c'est le cas... Cela ne devrait - il pas être possible dans un langage de programmation fonctionnel?

Alors maintenant nous arrivons à l'opérateur"^"...

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

Je pense que l'effet de " ^ "est de verrouiller" x " à la dernière valeur qui lui est liée. N'est-t-il? Pourquoi ne pas simplement s'assurer que toutes les "correspondances" / affectations sont immuables comme Erlang lui-même?

Je m'habituais à ça...

36
demandé sur greggreg 2015-01-15 22:23:24

2 réponses

Les données dans Elixir sont toujours immuables, mais il y a quelques raccourcis, qui vous permettent de taper moins ou ne vous inquiétez pas de trouver de nouveaux noms. Dans Erlang, vous pouvez souvent voir du code comme ceci:

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

Dans Elixir, vous pouvez simplement écrire:

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

C'est exactement la même chose, mais ça a l'air un peu mieux. Bien sûr, les meilleures solutions seraient d'écrire comme ceci:

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

Chaque fois que vous attribuez une nouvelle chose à la variable list, vous obtenez une nouvelle 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

Vous dites simplement que vous n'êtes plus intéressé par l'ancien a et laissez-le pointer vers autre chose. Si vous venez de L'arrière-plan Erlang, vous connaissez probablement la fonction f de shell.

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

Dans Élixir vous n'avez pas à écrire le f. C'est fait automatiquement pour vous. Cela signifie que chaque fois que vous avez une variable sur le côté gauche de la correspondance de motif, vous lui attribuez une nouvelle valeur.

Sans l'opérateur ^, Vous ne seriez pas en mesure d'avoir une variable sur le côté gauche de la correspondance de motif, car il obtiendrait une nouvelle valeur du côté droit. ^ signifie n'attribuez pas de nouvelles choses à cette variable-traitez-la comme une valeur littérale .

C'est pourquoi, dans Élixir

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

Est équivalent en Erlang à:

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

Et:

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

Est équivalent à:

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

Qui en Erlang est:

X = 1,
[1, X, 3] = [1, 2, 3]
48
répondu tkowal 2015-01-16 00:00:33

Les données dans elixir sont immuables, les variables sont cependant réattribuables. Ce qui peut rendre elixir légèrement déroutant est l'affectation combinée et la correspondance de modèle que vous voyez.

Lorsque vous utilisez le signe égal avec une référence de variable sur la gauche elixir premier modèle correspond à la structure, puis effectuer une affectation. Lorsque vous avez juste une seule référence de variable sur la gauche, elle correspondra à n'importe quelle structure et sera donc assignée comme ceci:

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

Quand vous avez un plus la structure complexe sur l'élixir gauche correspondra d'abord aux structures, puis effectuera l'affectation.

[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

Si vous voulez faire correspondre le contenu d'une variable, vous pouvez utiliser le code 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)**

Cet exemple artificiel aurait aussi pu être écrit avec 'a' sur le côté droit et sans le code pin:

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

Disons maintenant que vous vouliez assigner une variable à une partie d'une structure mais seulement si une partie de cette structure correspond à quelque chose stocké dans 'A', dans elixir this est trivial:

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

Dans ces exemples simples, l'utilisation de la broche et de la correspondance de motif n'est pas immédiatement très précieuse, mais à mesure que vous apprenez plus d'élixir et commencez à correspondre de plus en plus, cela fait partie de l'expressivité qu'elixir offre.

20
répondu greggreg 2016-03-23 15:04:46