Was ist der Unterschied zwischen a var
und val
Definition in Scala und warum braucht die Sprache beides? Warum würden Sie ein val
über ein var
und umgekehrt wählen ?
Was ist der Unterschied zwischen a var
und val
Definition in Scala und warum braucht die Sprache beides? Warum würden Sie ein val
über ein var
und umgekehrt wählen ?
Antworten:
Wie so viele andere gesagt haben, kann das einer zugewiesene Objekt val
nicht ersetzt werden, und das einer var
Dose zugewiesene Objekt . Der interne Zustand des Objekts kann jedoch geändert werden. Zum Beispiel:
class A(n: Int) {
var value = n
}
class B(n: Int) {
val value = new A(n)
}
object Test {
def main(args: Array[String]) {
val x = new B(5)
x = new B(6) // Doesn't work, because I can't replace the object created on the line above with this new one.
x.value = new A(6) // Doesn't work, because I can't replace the object assigned to B.value for a new one.
x.value.value = 6 // Works, because A.value can receive a new object.
}
}
Obwohl wir das zugewiesene Objekt nicht ändern können x
, können wir den Status dieses Objekts ändern. An der Wurzel lag jedoch ein var
.
Unveränderlichkeit ist aus vielen Gründen eine gute Sache. Erstens, wenn ein Objekt den internen Status nicht ändert, müssen Sie sich keine Sorgen machen, wenn ein anderer Teil Ihres Codes es ändert. Zum Beispiel:
x = new B(0)
f(x)
if (x.value.value == 0)
println("f didn't do anything to x")
else
println("f did something to x")
Dies ist besonders wichtig bei Multithread-Systemen. In einem Multithread-System kann Folgendes passieren:
x = new B(1)
f(x)
if (x.value.value == 1) {
print(x.value.value) // Can be different than 1!
}
Wenn Sie val
ausschließlich unveränderliche Datenstrukturen verwenden (dh Arrays, alles in scala.collection.mutable
usw. vermeiden ), können Sie sicher sein, dass dies nicht der Fall ist. Das heißt, es sei denn, es gibt Code, vielleicht sogar ein Framework, das Reflexionstricks ausführt - Reflexion kann leider "unveränderliche" Werte ändern.
Das ist ein Grund, aber es gibt noch einen anderen Grund dafür. Wenn Sie verwenden var
, können Sie versucht sein, dasselbe var
für mehrere Zwecke wiederzuverwenden. Dies hat einige Probleme:
Einfach ausgedrückt ist die Verwendung val
sicherer und führt zu besser lesbarem Code.
Wir können also in die andere Richtung gehen. Wenn val
das besser ist, warum var
überhaupt? Nun, einige Sprachen haben diesen Weg eingeschlagen, aber es gibt Situationen, in denen die Veränderlichkeit die Leistung erheblich verbessert.
Nehmen Sie zum Beispiel eine unveränderliche Queue
. Wenn Sie entweder enqueue
oder dequeue
Dinge darin, erhalten Sie ein neues Queue
Objekt. Wie würden Sie dann alle darin enthaltenen Elemente verarbeiten?
Ich werde das mit einem Beispiel durchgehen. Angenommen, Sie haben eine Warteschlange mit Ziffern und möchten daraus eine Zahl zusammensetzen. Wenn ich beispielsweise eine Warteschlange mit 2, 1, 3 in dieser Reihenfolge habe, möchte ich die Nummer 213 zurückerhalten. Lösen wir sie zunächst mit mutable.Queue
:
def toNum(q: scala.collection.mutable.Queue[Int]) = {
var num = 0
while (!q.isEmpty) {
num *= 10
num += q.dequeue
}
num
}
Dieser Code ist schnell und einfach zu verstehen. Der Hauptnachteil besteht darin, dass die übergebene Warteschlange von geändert wird toNum
, sodass Sie zuvor eine Kopie davon erstellen müssen. Das ist die Art von Objektverwaltung, von der Unveränderlichkeit Sie frei macht.
Jetzt verdecken wir es zu einem immutable.Queue
:
def toNum(q: scala.collection.immutable.Queue[Int]) = {
def recurse(qr: scala.collection.immutable.Queue[Int], num: Int): Int = {
if (qr.isEmpty)
num
else {
val (digit, newQ) = qr.dequeue
recurse(newQ, num * 10 + digit)
}
}
recurse(q, 0)
}
Da ich eine Variable nicht wiederverwenden kann, um meine zu verfolgen num
, wie im vorherigen Beispiel, muss ich auf die Rekursion zurückgreifen. In diesem Fall handelt es sich um eine Schwanzrekursion, die eine ziemlich gute Leistung aufweist. Dies ist jedoch nicht immer der Fall: Manchmal gibt es einfach keine gute (lesbare, einfache) Lösung für die Schwanzrekursion.
Beachten Sie jedoch, dass ich diesen Code neu schreiben kann, um ein immutable.Queue
und ein var
gleichzeitig zu verwenden! Zum Beispiel:
def toNum(q: scala.collection.immutable.Queue[Int]) = {
var qr = q
var num = 0
while (!qr.isEmpty) {
val (digit, newQ) = qr.dequeue
num *= 10
num += digit
qr = newQ
}
num
}
Dieser Code ist immer noch effizient, erfordert keine Rekursion und Sie müssen sich keine Sorgen machen, ob Sie vor dem Aufruf eine Kopie Ihrer Warteschlange erstellen müssen oder nicht toNum
. Natürlich habe ich es vermieden, Variablen für andere Zwecke wiederzuverwenden, und kein Code außerhalb dieser Funktion sieht sie, sodass ich mir keine Sorgen machen muss, dass sich ihre Werte von einer Zeile zur nächsten ändern - außer wenn ich dies ausdrücklich tue.
Scala entschied sich dafür, den Programmierer dies tun zu lassen, wenn der Programmierer dies für die beste Lösung hielt. Andere Sprachen haben sich dafür entschieden, solchen Code schwierig zu machen. Der Preis, den Scala (und jede Sprache mit weit verbreiteter Veränderlichkeit) zahlt, ist, dass der Compiler nicht so viel Spielraum bei der Optimierung des Codes hat, wie er es sonst könnte. Die Antwort von Java darauf besteht darin, den Code basierend auf dem Laufzeitprofil zu optimieren. Wir könnten auf jeder Seite über Vor- und Nachteile sprechen.
Persönlich denke ich, dass Scala vorerst die richtige Balance findet. Es ist bei weitem nicht perfekt. Ich denke, sowohl Clojure als auch Haskell haben sehr interessante Vorstellungen, die nicht von Scala übernommen wurden, aber Scala hat auch ihre eigenen Stärken. Wir werden sehen, was in der Zukunft auf uns zukommt.
q
. Es wird eine Kopie des Verweises auf dieses Objekt erstellt - auf dem Stapel, nicht auf dem Heap . Was die Leistung betrifft, müssen Sie klarer sein, von welchem "es" Sie sprechen.
(x::xs).drop(1)
ist genau xs
keine "Kopie" von xs
) von diesem Link konnte ich verstehen. tnx!
qr
es sich um eine unveränderliche Warteschlange handelt, wird bei jedem qr.dequeue
Aufruf des Ausdrucks ein new Queue
(siehe < github.com/scala/scala/blob/2.13.x/src/library/scala/collection/… ) erstellt.
val
ist endgültig, kann also nicht gesetzt werden. Denken Sie final
in Java.
val
Variablen unveränderlich, aber die Objekte, auf die sie verweisen, müssen es nicht sein. Laut dem Link, den Stefan gepostet hat: "Hier kann die Namensreferenz nicht geändert werden, um auf ein anderes Array zu verweisen, aber das Array selbst kann geändert werden. Mit anderen Worten, der Inhalt / die Elemente des Arrays können geändert werden." Es ist also so, wie es final
in Java funktioniert.
+=
auf eine veränderliche Hashmap zurückgreifen, die als val
gut definiert ist - ich glaube, genau so final
funktioniert es in Java
In einfachen Worten:
var = var iable
val = v ariable + fin al
Der Unterschied besteht darin, dass a var
neu zugewiesen werden kann, während a val
nicht zugewiesen werden kann. Die Veränderbarkeit oder das Gegenteil von dem, was tatsächlich zugewiesen ist, ist ein Nebenproblem:
import collection.immutable
import collection.mutable
var m = immutable.Set("London", "Paris")
m = immutable.Set("New York") //Reassignment - I have change the "value" at m.
Wohingegen:
val n = immutable.Set("London", "Paris")
n = immutable.Set("New York") //Will not compile as n is a val.
Und daher:
val n = mutable.Set("London", "Paris")
n = mutable.Set("New York") //Will not compile, even though the type of n is mutable.
Wenn Sie eine Datenstruktur erstellen und alle Felder val
s sind, ist diese Datenstruktur daher unveränderlich, da sich ihr Status nicht ändern kann.
val
bedeutet unveränderlich und var
bedeutet veränderlich.
Denken in C ++,
val x: T
ist analog zum konstanten Zeiger auf nicht konstante Daten
T* const x;
während
var x: T
ist analog zum nicht konstanten Zeiger auf nicht konstante Daten
T* x;
Die Bevorzugung val
gegenüber var
erhöht die Unveränderlichkeit der Codebasis, was ihre Korrektheit, Parallelität und Verständlichkeit erleichtern kann.
Um die Bedeutung eines konstanten Zeigers auf nicht konstante Daten zu verstehen, betrachten Sie das folgende Scala-Snippet:
val m = scala.collection.mutable.Map(1 -> "picard")
m // res0: scala.collection.mutable.Map[Int,String] = HashMap(1 -> picard)
Hier ist der "Zeiger" val m
konstant, so dass wir ihn nicht neu zuweisen können, um auf etwas anderes zu zeigen
m = n // error: reassignment to val
Wir können jedoch tatsächlich die nicht konstanten Daten selbst ändern, die darauf hinweisen, dass dies m
gefällt
m.put(2, "worf")
m // res1: scala.collection.mutable.Map[Int,String] = HashMap(1 -> picard, 2 -> worf)
"val bedeutet unveränderlich und var bedeutet veränderlich."
Um es zu paraphrasieren: "val bedeutet Wert und var bedeutet Variable".
Eine Unterscheidung, die beim Rechnen äußerst wichtig ist (weil diese beiden Konzepte die Essenz dessen definieren, worum es beim Programmieren geht), und dass OO es geschafft hat, fast vollständig zu verschwimmen, weil in OO das einzige Axiom ist, dass "alles ein ist Objekt". Infolgedessen neigen viele Programmierer heutzutage dazu, nicht zu verstehen / zu schätzen / zu erkennen, weil sie einer Gehirnwäsche unterzogen wurden, um ausschließlich "nach OO-Art zu denken". Dies führt häufig dazu, dass variable / veränderbare Objekte wie überall verwendet werden , wenn Werte / unveränderliche Objekte möglicherweise / besser gewesen wären.
val bedeutet unveränderlich und var bedeutet veränderlich
Sie können val
als Java Programmiersprache final
Schlüsselwelt oder C ++ Sprache const
Schlüsselwelt denken 。
Val
bedeutet, dass es endgültig ist , kann nicht neu zugewiesen werden
Während, Var
kann später neu zugewiesen .
Es ist so einfach wie es heißt.
var bedeutet, dass es variieren kann
val bedeutet unveränderlich
Werte sind typisierte Speicherkonstanten. Einmal erstellt, kann sein Wert nicht neu zugewiesen werden. Mit dem Schlüsselwort val kann ein neuer Wert definiert werden.
z.B. val x: Int = 5
Hier ist der Typ optional, da Scala ihn aus dem zugewiesenen Wert ableiten kann.
Var - Variablen sind typisierte Speichereinheiten, denen wieder Werte zugewiesen werden können, solange Speicherplatz reserviert ist.
z.B. var x: Int = 5
In beiden Speichereinheiten gespeicherte Daten werden von JVM automatisch freigegeben, sobald diese nicht mehr benötigt werden.
In Scala werden Werte aufgrund der Stabilität gegenüber Variablen bevorzugt, was den Code insbesondere im gleichzeitigen und Multithread-Code beeinflusst.
Obwohl viele bereits den Unterschied zwischen Val und var beantwortet haben . Es ist jedoch zu beachten, dass val nicht genau dem endgültigen Schlüsselwort entspricht.
Wir können den Wert von val mithilfe der Rekursion ändern, aber wir können den Wert von final niemals ändern. Final ist konstanter als Val.
def factorial(num: Int): Int = {
if(num == 0) 1
else factorial(num - 1) * num
}
Methodenparameter sind standardmäßig val und werden bei jedem Aufruf geändert.
var qr = q
eine Kopie vonq
?