В чем разница между a varи valопределением в Scala и почему язык нуждается в обоих? Почему вы выбрали бы valболее varи наоборот?
В чем разница между a varи valопределением в Scala и почему язык нуждается в обоих? Почему вы выбрали бы valболее varи наоборот?
Ответы:
Как говорили многие другие, объект, назначенный valэлементу, не может быть заменен, а объект, назначенный элементу varcan. Однако у указанного объекта может быть изменено его внутреннее состояние. Например:
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.
}
}
Таким образом, даже если мы не можем изменить объект, назначенный x, мы можем изменить состояние этого объекта. Корень этого, однако, был var.
Теперь неизменность - это хорошая вещь по многим причинам. Во-первых, если объект не изменяет внутреннее состояние, вам не нужно беспокоиться, если какая-то другая часть вашего кода изменит его. Например:
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")
Это становится особенно важным с многопоточными системами. В многопоточной системе может произойти следующее:
x = new B(1)
f(x)
if (x.value.value == 1) {
print(x.value.value) // Can be different than 1!
}
Если вы используете valисключительно и используете только неизменяемые структуры данных (то есть избегаете массивов, всего внутри scala.collection.mutableи т. Д.), Вы можете быть уверены, что этого не произойдет. То есть, если какой-то код, возможно, даже каркас, выполняет трюки отражения - отражение, к сожалению, может изменить «неизменяемые» значения.
Это одна из причин, но есть и другая причина. Когда вы используете var, вы можете соблазниться к повторному использованию одного и того же varдля нескольких целей. Это имеет некоторые проблемы:
Проще говоря, использование valбезопаснее и приводит к более читаемому коду.
Тогда мы можем пойти в другом направлении. Если valэто лучше, то зачем varвообще? Ну, некоторые языки пошли по этому пути, но бывают ситуации, когда изменчивость значительно повышает производительность.
Например, взять неизменный Queue. Когда вы enqueueили dequeueвещи в нем, вы получаете новый Queueобъект. Как тогда, вы бы пошли на обработку всех предметов в нем?
Я пройдусь по этому примеру. Допустим, у вас есть очередь цифр, и вы хотите составить из них число. Например, если у меня есть очередь с 2, 1, 3, в этом порядке, я хочу вернуть число 213. Давайте сначала решим это с помощью mutable.Queue:
def toNum(q: scala.collection.mutable.Queue[Int]) = {
var num = 0
while (!q.isEmpty) {
num *= 10
num += q.dequeue
}
num
}
Этот код быстр и прост для понимания. Его главный недостаток заключается в том, что переданная очередь модифицируется toNum, поэтому вы должны сделать ее копию заранее. Это тот тип управления объектами, который делает вас неизменным.
Теперь давайте перейдем к 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)
}
Поскольку я не могу повторно использовать некоторую переменную для отслеживания моей num, как в предыдущем примере, мне нужно прибегнуть к рекурсии. В данном случае это хвостовая рекурсия, которая имеет довольно хорошую производительность. Но это не всегда так: иногда просто не существует хорошего (удобочитаемого, простого) решения для хвостовой рекурсии.
Обратите внимание, однако, что я могу переписать этот код, чтобы использовать одновременно immutable.Queueи a var! Например:
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
}
Этот код по-прежнему эффективен, не требует рекурсии, и вам не нужно беспокоиться о том, нужно ли вам делать копию своей очереди или нет перед вызовом toNum. Естественно, я избегал повторного использования переменных для других целей, и ни один код за пределами этой функции их не видит, поэтому мне не нужно беспокоиться об изменении их значений от одной строки к другой - за исключением случаев, когда я делаю это явно.
Scala решила позволить программисту сделать это, если программист посчитал это лучшим решением. Другие языки решили сделать такой код сложным. Цена, которую платит Scala (и любой язык с широко распространенной изменчивостью), заключается в том, что у компилятора не так много возможностей для оптимизации кода, как в противном случае. Ответ Java на это - оптимизация кода на основе профиля времени выполнения. Мы могли бы говорить о плюсах и минусах каждой стороны.
Лично я думаю, что Скала пока устанавливает правильный баланс. Это не идеально, безусловно. Я думаю, что и у Clojure, и у Haskell есть очень интересные понятия, не принятые Scala, но у Scala есть и свои сильные стороны. Посмотрим, что будет в будущем.
q. Он делает копию - в стеке, а не в куче - ссылки на этот объект. Что касается производительности, вам нужно будет более четко понимать, о чем «это» вы говорите.
(x::xs).drop(1)это точно xs, а не «копия» xs) отсюда ссылка, которую я мог понять. TNX!
qrявляется неизменной, каждый раз, когда qr.dequeueвызывается выражение, оно создает new Queue(см. < Github.com/scala/scala/blob/2.13.x/src/library/scala/collection/… ).
valявляется окончательным, то есть не может быть установлен. Думай finalв яве.
val переменные являются неизменяемыми, но объекты, на которые они ссылаются, необязательно должны быть. По ссылке Стефан написал: «Здесь ссылка на имена не может быть изменена, чтобы указывать на другой массив, но сам массив может быть изменен. Другими словами, содержимое / элементы массива могут быть изменены». Так что это похоже на то, как finalработает в Java.
+=изменяемый hashmap, определенный как очень valхороший - я верю, что именно так finalработает в Java
Проще говоря:
вар = переменная iable
val = v ariable + fin al
Разница в том, что а varможно переназначить, а valнельзя. Изменчивость, или что-то еще из того, что фактически назначено, является побочной проблемой:
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.
В то время как:
val n = immutable.Set("London", "Paris")
n = immutable.Set("New York") //Will not compile as n is a val.
И поэтому:
val n = mutable.Set("London", "Paris")
n = mutable.Set("New York") //Will not compile, even though the type of n is mutable.
Если вы строите структуру данных и все ее поля имеют значение vals, то эта структура данных является неизменной, поскольку ее состояние не может измениться.
val означает неизменный и var означает изменчивый.
Мышление с точки зрения C ++,
val x: T
аналог константного указателя на непостоянные данные
T* const x;
пока
var x: T
аналог непостоянного указателя на непостоянные данные
T* x;
Любить valболееvar увеличивает неизменность кодовой базы, что может способствовать его правильности, параллелизму и понятности.
Чтобы понять смысл наличия постоянного указателя на непостоянные данные, рассмотрим следующий фрагмент Scala:
val m = scala.collection.mutable.Map(1 -> "picard")
m // res0: scala.collection.mutable.Map[Int,String] = HashMap(1 -> picard)
Здесь «указатель» val m является постоянным, поэтому мы не можем переназначить его так, чтобы он указывал на что-то подобное
m = n // error: reassignment to val
Однако мы действительно можем изменить сами непостоянные данные, которые mуказывают на как
m.put(2, "worf")
m // res1: scala.collection.mutable.Map[Int,String] = HashMap(1 -> picard, 2 -> worf)
«val означает неизменный, а var означает изменяемый».
Перефразируя, «val означает значение, а var означает переменную».
Различие, которое оказывается чрезвычайно важным в вычислениях (потому что эти два понятия определяют саму суть всего, что связано с программированием), и что ОО удалось почти полностью размывать, потому что в ОО единственная аксиома заключается в том, что «все является объект». И как следствие, многие программисты в наши дни склонны не понимать / ценить / распознавать, потому что им «промыли мозги» исключительно для «осмысления ОО-пути». Часто это приводит к тому, что переменные / изменяемые объекты используются, как и везде , когда значения / неизменяемые объекты могут / часто были бы лучше.
val означает неизменяемый, а var означает изменяемый
Вы можете думать valкак finalключевой мир языка программирования Java или constключевой мир языка C ++ 。
Valзначит его окончательный , нельзя переназначен
Принимая во внимание, что Varможет быть переназначен позже .
Это так просто, как назвать.
var означает, что может меняться
val означает неизменный
Val - значения являются типизированными константами хранения. После создания его значение не может быть переназначено. новое значение можно определить с помощью ключевого слова val.
например. val x: Int = 5
Здесь type является необязательным, поскольку scala может вывести его из заданного значения.
Переменные - это типизированные единицы хранения, которым можно снова присвоить значения, если зарезервировано место в памяти.
например. var x: Int = 5
Данные, хранящиеся в обеих единицах хранения, автоматически удаляются JVM, когда они больше не нужны.
В scala значения предпочтительнее переменных из-за стабильности, что приводит к коду, особенно в параллельном и многопоточном коде.
Хотя многие уже ответили на разницу между Val и var . Но следует отметить, что val не совсем как final ключевое слово.
Мы можем изменить значение val с помощью рекурсии, но мы никогда не сможем изменить значение final. Финал более постоянен, чем Вал.
def factorial(num: Int): Int = {
if(num == 0) 1
else factorial(num - 1) * num
}
Параметры метода по умолчанию - val, и при каждом вызове значение изменяется.
var qr = qкопияq?