Gibt es in Scala Richtlinien, wann val mit einer veränderlichen Sammlung verwendet werden soll, während var mit einer unveränderlichen Sammlung verwendet werden soll? Oder sollten Sie wirklich Wert auf eine unveränderliche Sammlung legen?
Die Tatsache, dass es beide Arten von Sammlungen gibt, gibt mir eine große Auswahl, und oft weiß ich nicht, wie ich diese Auswahl treffen soll.
scala
collections
functional-programming
immutability
James McCabe
quelle
quelle
Antworten:
Ziemlich häufige Frage, diese. Das Schwierige ist, die Duplikate zu finden.
Sie sollten sich um referenzielle Transparenz bemühen . Was das bedeutet , ist , dass, wenn ich einen Ausdruck „e“ haben, könnte ich eine machen
val x = e
, und ersetzene
mitx
. Dies ist die Eigenschaft, die die Veränderlichkeit beeinträchtigt. Wenn Sie eine Entwurfsentscheidung treffen müssen, maximieren Sie die referenzielle Transparenz.In der Praxis ist eine lokale Methode
var
die sicherstevar
, die es gibt, da sie der Methode nicht entgeht. Wenn die Methode kurz ist, noch besser. Wenn dies nicht der Fall ist, versuchen Sie, es durch Extrahieren anderer Methoden zu reduzieren.Auf der anderen Seite hat ein veränderliches Sammlung das Potenzial zu entkommen, auch wenn dies nicht der Fall. Wenn Sie Code ändern, möchten Sie ihn möglicherweise an andere Methoden übergeben oder zurückgeben. So etwas bricht die referenzielle Transparenz.
Auf einem Objekt (einem Feld) passiert so ziemlich dasselbe, aber mit schlimmeren Konsequenzen. In beiden Fällen hat das Objekt den Status und unterbricht daher die referenzielle Transparenz. Eine veränderbare Sammlung bedeutet jedoch, dass sogar das Objekt selbst die Kontrolle darüber verlieren kann, wer es ändert.
quelle
immutable val
überimmutable var
alsmutable val
übermutable var
. Besondersimmutable var
vorbeimutable val
!var
. Ein weiteres nettes Merkmal der Verwendung unveränderlicher Sammlungen ist, dass Sie alte Kopien effizient aufbewahren können, selbst wenn dievar
mutiert sind.var x: Set[Int]
alsval x: mutable.Set[Int]
wenn Siex
zu einer anderen Funktion übergehen , im ersteren Fall sind Sie sicher, dass diese Funktionx
für Sie nicht mutieren kann.Wenn Sie mit unveränderlichen Sammlungen arbeiten und diese "ändern" müssen, z. B. Elemente in einer Schleife hinzufügen, müssen Sie
var
s verwenden, da Sie die resultierende Sammlung irgendwo speichern müssen. Wenn Sie nur aus unveränderlichen Sammlungen lesen, verwenden Sieval
s.Stellen Sie im Allgemeinen sicher, dass Sie Referenzen und Objekte nicht verwechseln.
val
s sind unveränderliche Referenzen (konstante Zeiger in C). Das heißt, wenn Sie verwendenval x = new MutableFoo()
, werden Sie in der Lage , das Objekt zu ändern , dassx
Punkte, aber Sie werden nicht in der Lage zu ändern , auf diex
Objektpunkte. Das Gegenteil gilt, wenn Sie verwendenvar x = new ImmutableFoo()
. Ich nehme meinen ersten Rat auf: Wenn Sie nicht ändern müssen, auf welches Objekt ein Referenzpunkt zeigt, verwenden Sieval
s.quelle
var immutable = something(); immutable = immutable.update(x)
vereitelt den Zweck der Verwendung einer unveränderlichen Sammlung. Sie haben die referenzielle Transparenz bereits aufgegeben und können normalerweise den gleichen Effekt aus einer veränderlichen Sammlung mit besserer Zeitkomplexität erzielen. Von den vier Möglichkeiten (val
undvar
veränderlich und unveränderlich) ist diese am wenigsten sinnvoll. Ich benutze oftval mutable
.var list: List[X] = Nil; list = item :: list; ...
und ich hatte vergessen, dass ich einmal anders geschrieben habe.Der beste Weg, dies zu beantworten, ist mit einem Beispiel. Nehmen wir an, wir haben einen Prozess, bei dem aus irgendeinem Grund einfach Zahlen gesammelt werden. Wir möchten diese Nummern protokollieren und senden die Sammlung dazu an einen anderen Prozess.
Natürlich sammeln wir immer noch Zahlen, nachdem wir die Sammlung an den Logger gesendet haben. Angenommen, der Protokollierungsprozess verursacht einen gewissen Aufwand, der die eigentliche Protokollierung verzögert. Hoffentlich können Sie sehen, wohin das führt.
Wenn wir diese Sammlung in einer veränderlichen Sammlung speichern
val
(veränderbar, weil wir sie kontinuierlich erweitern), bedeutet dies, dass der Prozess, der die Protokollierung durchführt, dasselbe Objekt betrachtet , das von unserem Erfassungsprozess noch aktualisiert wird. Diese Sammlung kann jederzeit aktualisiert werden. Wenn also Zeit für die Protokollierung ist, protokollieren wir die von uns gesendete Sammlung möglicherweise nicht.Wenn wir
var
eine unveränderliche Datenstruktur verwenden , senden wir eine unveränderliche Datenstruktur an den Logger. Wenn wir unserer Sammlung weitere Zahlen hinzufügen, werden wir unsere durch eine neue unveränderliche Datenstruktur ersetzen . Dies bedeutet nicht, dass die an den Logger gesendete Sammlung ersetzt wird! Es verweist immer noch auf die Sammlung, die gesendet wurde. Unser Logger protokolliert also tatsächlich die empfangene Sammlung.var
quelle
Ich denke, die Beispiele in diesem Blog-Beitrag werden mehr Licht ins Dunkel bringen, da die Frage, welche Kombination verwendet werden soll, in Parallelitätsszenarien noch wichtiger wird: Bedeutung der Unveränderlichkeit für die Parallelität . Und wenn wir schon dabei sind, beachten Sie die bevorzugte Verwendung von synchronisiertem vs @volatile vs etwas wie AtomicReference: drei Tools
quelle
var immutable
vs.val mutable
Neben vielen hervorragenden Antworten auf diese Frage. Hier ist ein einfaches Beispiel, das mögliche Gefahren von Folgendem veranschaulicht
val mutable
:Veränderbare Objekte können innerhalb von Methoden geändert werden, die sie als Parameter verwenden, während eine Neuzuweisung nicht zulässig ist.
Ergebnis:
So etwas kann nicht passieren
var immutable
, da eine Neuzuweisung nicht zulässig ist:Ergebnisse in:
Da Funktionsparameter so behandelt werden, ist
val
diese Neuzuweisung nicht zulässig.quelle
mutable val
mit dem nicht möglich istimmutable var
. Was ist hier falsch?+=
Methode wie Array-Puffer. Ihre Antworten implizieren, dass dies+=
dasselbe ist,x = x + y
was es nicht ist. Ihre Aussage, dass Funktionsparameter als vals behandelt werden, ist korrekt und Sie erhalten den von Ihnen erwähnten Fehler, jedoch nur, weil Sie ihn verwendet haben=
. Sie können denselben Fehler mit einem ArrayBuffer erhalten, sodass die Veränderbarkeit der Sammlungen hier nicht wirklich relevant ist. Es ist also keine gute Antwort, weil es nicht zu dem kommt, worüber das OP spricht. Obwohl es ein gutes Beispiel für die Gefahren ist, eine veränderliche Sammlung weiterzugeben, wenn Sie dies nicht beabsichtigt haben.ArrayBuffer
, nicht replizieren , indem Sie verwendenVector
. Die Frage des OP ist weit gefasst, aber sie suchten nach Vorschlägen, wann sie verwendet werden sollten. Daher halte ich meine Antwort für nützlich, da sie die Gefahren der Weitergabe veränderlicher Sammlungen aufzeigt (die Tatsache, dassval
dies nicht hilft).immutable var
ist sicherer alsmutable val
.