In Dave Thomas 'Buch Programming Elixir erklärt er "Elixir erzwingt unveränderliche Daten" und fährt fort:
Wenn eine Variable in Elixir auf eine Liste wie [1,2,3] verweist, wissen Sie, dass sie immer auf dieselben Werte verweist (bis Sie die Variable erneut binden).
Das klingt wie "es wird sich nie ändern, wenn Sie es nicht ändern", also bin ich verwirrt darüber, was der Unterschied zwischen Veränderlichkeit und erneuter Bindung ist. Ein Beispiel, das die Unterschiede hervorhebt, wäre sehr hilfreich.
immutability
elixir
Odhran Roche
quelle
quelle
Antworten:
Unveränderlichkeit bedeutet, dass sich Datenstrukturen nicht ändern. Zum Beispiel gibt die Funktion
HashSet.new
eine leere Menge zurück und solange Sie an der Referenz auf diese Menge festhalten, wird sie niemals nicht leer. Was Sie in Elixir jedoch tun können , ist, eine variable Referenz auf etwas wegzuwerfen und sie erneut an eine neue Referenz zu binden. Zum Beispiel:Was nicht passieren kann , ist, dass sich der Wert unter dieser Referenz ändert, ohne dass Sie ihn explizit neu binden:
Vergleichen Sie dies mit Ruby, wo Sie Folgendes tun können:
quelle
^
. Rebind ist in Elixir nur ein ausgefallener Begriff, um zu verbergen, dass eine Variable tatsächlich veränderlich ist. Zu bedenken, dass ich Elixir liebe, aber ich mag es wirklich nicht, wenn die Community versucht, die Veränderlichkeit der Variablen hinter ausgefallenen Begriffen und Erklärungen zu verbergen.^
in einem Ausdruck so vorstellen, dass er nur die Variable durch ihren Wert ersetzt. Dieser Operator findet den größten Teil seiner Verwendung beim Mustervergleich usw. Was die Unveränderlichkeit betrifft, so ist Elixir in jeder Hinsicht unveränderlich. Wenn der Wert einer Variablen geändert werden soll, kann der Wert, der an der Stelle gespeichert ist, auf die diese Variable zeigt, nicht selbst zu einem anderen Wert mutiert werden. Stattdessen wird die Variable an einen neuen Speicherort zurückgebunden, an dem der neue Wert gespeichert werden kann. Daher der Begriff "neu binden".Stellen Sie sich "Variablen" in Elixir nicht als Variablen in imperativen Sprachen vor, "Leerzeichen für Werte". Betrachten Sie sie eher als "Bezeichnungen für Werte".
Vielleicht sollten Sie es besser verstehen, wenn Sie sich ansehen, wie Variablen ("Labels") in Erlang funktionieren. Immer wenn Sie ein "Label" an einen Wert binden, bleibt es für immer daran gebunden (hier gelten natürlich die Bereichsregeln).
In Erlang kann man das nicht schreiben:
Stattdessen müssen Sie Folgendes schreiben:
Wie Sie sehen, ist dies sehr unpraktisch, hauptsächlich für das Refactoring von Code. Wenn Sie nach der ersten Zeile eine neue Zeile einfügen möchten, müssen Sie alle v * neu nummerieren oder etwas wie "v1a = ..." schreiben.
In Elixir können Sie also Variablen neu binden (die Bedeutung des "Labels" ändern), hauptsächlich zur Vereinfachung:
Zusammenfassung: In imperativen Sprachen sind Variablen wie benannte Koffer: Sie haben einen Koffer mit dem Namen "v". Zuerst legst du ein Sandwich hinein. Dann legst du einen Apfel hinein (das Sandwich ist verloren und wird vielleicht vom Müllsammler gegessen). In Erlang und Elixir ist die Variable kein Ort, an dem etwas eingefügt werden kann. Sie ist nur ein Name / eine Bezeichnung für einen Wert. In Elixir können Sie die Bedeutung der Bezeichnung ändern. In Erlang können Sie nicht.Dies ist der Grund, warum es weder in Erlang noch in Elixir sinnvoll ist, "Speicher für eine Variable zuzuweisen", da Variablen keinen Speicherplatz belegen. Werte tun.Jetzt sehen Sie vielleicht den Unterschied deutlich.
Wenn Sie tiefer graben möchten:
1) Sehen Sie sich an, wie "ungebundene" und "gebundene" Variablen in Prolog funktionieren. Dies ist die Quelle dieses vielleicht etwas seltsamen Erlang-Konzepts von "Variablen, die nicht variieren".
2) Beachten Sie, dass "=" in Erlang wirklich kein Zuweisungsoperator ist, sondern nur ein Übereinstimmungsoperator! Wenn Sie eine ungebundene Variable mit einem Wert abgleichen, binden Sie die Variable an diesen Wert. Das Abgleichen einer gebundenen Variablen entspricht dem Abgleichen eines Werts, an den sie gebunden ist. Dies führt also zu einem Übereinstimmungsfehler :
3) Bei Elixir ist das nicht der Fall. In Elixir muss es also eine spezielle Syntax geben, um den Abgleich zu erzwingen:
quelle
x = x +1
tut meinem mathematischen Gehirn weh. Erlang stoppt diese Kopfschmerzen und Elixir nicht. :(Erlang und offensichtlich Elixier, das darauf aufgebaut ist, umfasst Unveränderlichkeit. Sie erlauben einfach nicht, dass sich Werte an einem bestimmten Speicherort ändern.Niemals, bis die Variable Müll gesammelt bekommt oder außerhalb des Gültigkeitsbereichs liegt.
Variablen sind nicht unveränderlich. Die Daten, auf die sie verweisen, sind unveränderlich. Aus diesem Grund wird das Ändern einer Variablen als erneutes Binden bezeichnet.
Sie richten es auf etwas anderes und ändern nicht das, worauf es zeigt.
x = 1
gefolgt vonx = 2
ändert nichts an den im Computerspeicher gespeicherten Daten, bei denen die 1 eine 2 war. Es setzt eine 2 an eine neue Stelle und zeigtx
darauf.x
ist jeweils nur für einen Prozess zugänglich, sodass dies keine Auswirkungen auf die Parallelität hat. Parallelität ist der wichtigste Ort, um sich überhaupt darum zu kümmern, ob etwas ohnehin unveränderlich ist.Durch das erneute Binden wird der Status eines Objekts überhaupt nicht geändert. Der Wert befindet sich immer noch am selben Speicherort. Die Bezeichnung (Variable) verweist jetzt auf einen anderen Speicherort, sodass die Unveränderlichkeit erhalten bleibt. Das erneute Binden ist in Erlang nicht verfügbar, aber während es sich in Elixir befindet, bremst dies dank seiner Implementierung keine von der Erlang-VM auferlegten Einschränkungen. Die Gründe für diese Wahl werden von Josè Valim in diesem Kern gut erklärt .
Angenommen, Sie hatten eine Liste
und Sie hatten einen anderen Prozess, der Listen nahm und dann wiederholt "Sachen" gegen sie ausführte und sie während dieses Prozesses zu ändern, wäre schlecht. Sie könnten diese Liste wie senden
Jetzt möchte Ihr nächster Code möglicherweise l mit mehr Werten für weitere Arbeiten aktualisieren, die nichts mit dem zu tun haben, was dieser andere Prozess tut.
Oh nein, jetzt wird dieser erste Prozess ein undefiniertes Verhalten haben, weil Sie die Liste geändert haben, oder? Falsch.
Diese ursprüngliche Liste bleibt unverändert. Was Sie wirklich getan haben, war, eine neue Liste basierend auf der alten zu erstellen und l wieder an diese neue Liste zu binden.
Der separate Prozess hat niemals Zugriff auf l. Die Daten, auf die ich ursprünglich verwiesen habe, sind unverändert, und der andere Prozess (vermutlich, sofern er nicht ignoriert wird) hat einen eigenen Verweis auf diese ursprüngliche Liste.
Was zählt, ist, dass Sie Daten nicht prozessübergreifend gemeinsam nutzen und dann ändern können, während ein anderer Prozess sie betrachtet. In einer Sprache wie Java, in der Sie einige veränderbare Typen haben (alle primitiven Typen plus Referenzen selbst), ist es möglich, eine Struktur / ein Objekt, die beispielsweise ein int enthält, gemeinsam zu nutzen und dieses int von einem Thread zu ändern, während ein anderer es liest.
Tatsächlich ist es möglich, einen großen Integer-Typ in Java teilweise zu ändern, während er von einem anderen Thread gelesen wird. Zumindest war es früher nicht sicher, ob sie diesen Aspekt der Dinge mit dem 64-Bit-Übergang eingeklemmt haben. Der Punkt ist jedenfalls, dass Sie den Teppich unter anderen Prozessen / Threads herausziehen können, indem Sie Daten an einer Stelle ändern, die beide gleichzeitig betrachten.
Das ist in Erlang und im weiteren Sinne in Elixir nicht möglich. Das bedeutet hier Unveränderlichkeit.
Um etwas genauer zu sein, in Erlang (die Originalsprache für VM Elixir läuft darauf) waren alles unveränderliche Variablen mit einfacher Zuweisung, und Elixir verbirgt ein Muster, das Erlang-Programmierer entwickelt haben, um dies zu umgehen.
Wenn in Erlang a = 3 ist, ist dies der Wert von a für die Dauer der Existenz dieser Variablen, bis sie aus dem Gültigkeitsbereich ausfällt und Müll gesammelt wird.
Dies war manchmal nützlich (nichts ändert sich nach der Zuweisung oder Musterübereinstimmung, so dass es leicht zu überlegen ist, was eine Funktion tut), aber auch etwas umständlich, wenn Sie während des Kurses, in dem eine Funktion ausgeführt wird, mehrere Dinge mit einer Variablen oder Sammlung tun.
Code würde oft so aussehen:
Dies war etwas klobig und machte das Refactoring schwieriger als nötig. Elixir tut dies hinter den Kulissen, versteckt es jedoch über Makros und Code-Transformationen, die vom Compiler ausgeführt werden, vor dem Programmierer.
Tolle Diskussion hier
Unveränderlichkeit im Elixier
quelle
x = 1; f = fn -> x end; x = 2; #=> 2
in Elixir machst ,f
greift das Lambda nicht einmal über die x-Variable zux
, sondern über eine sekundäre Referenz? Ist es so möglich, immer noch auf die 1 zuzugreifen, obwohl x auf 2 gesetzt wird? Mir ist klar, dass ein Teil davon vom Elixier stammt, das es sofort kompiliert, aber abgesehen von dieser Tatsache, gibt es eine sekundäre Referenz, die verwendet wird, um auch nach dem erneuten Binden auf Daten zuzugreifen?Die Variablen sind wirklich im Sinne unveränderlich, jede neue Neubindung (Zuweisung) ist nur für den Zugriff sichtbar, der danach kommt. Alle vorherigen Zugriffe beziehen sich immer noch auf alte Werte zum Zeitpunkt ihres Aufrufs.
quelle
Um es sehr einfach zu machen
Variablen in Elixir sind nicht wie Container, in denen Sie ständig Elemente zum Container hinzufügen, entfernen oder ändern.
Stattdessen ähneln sie Beschriftungen, die an einen Container angehängt sind. Wenn Sie eine Variable neu zuweisen, ist dies so einfach, dass Sie eine Beschriftung aus einem Container auswählen und auf einem neuen Container mit den erwarteten Daten platzieren.
quelle