Яка різниця між a var
та val
визначенням у Scala і чому мова потрібна обом? Чому б ви вибрали val
над var
і навпаки?
Яка різниця між a var
та val
визначенням у Scala і чому мова потрібна обом? Чому б ви вибрали val
над var
і навпаки?
Відповіді:
Як так багато інших говорили, об'єкт, призначений для val
не може бути замінений, і об'єкт, призначений var
банку. Однак зазначений об'єкт може змінити свій внутрішній стан. Наприклад:
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 (і будь-яка мова з широкомасштабною модифікацією) платить за те, що компілятор не має стільки свободи в оптимізації коду, як міг би інакше. Відповідь Java на це - оптимізація коду на основі профілю виконання. Ми можемо продовжувати питання щодо плюсів і мінусів для кожної сторони.
Особисто я вважаю, що Скала зараз справляє правильний баланс. Це далеко не ідеально. Я думаю, що і Клуджуре, і Хаскелл мають дуже цікаві поняття, не прийняті Скалою, але і у Скали є свої сильні сторони. Ми побачимо, що з’явиться в майбутньому.
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.
+=
на мутабельну хешмап, визначену як val
просто чудову - я вважаю, як саме вона final
працює в Java
Простими словами:
var = var 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.
Якщо ви будуєте структуру даних, і всі її поля є val
s, то ця структура даних є незмінною, оскільки її стан не може змінюватися.
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 означає змінну".
Відмінність, яка виявляється надзвичайно важливою в обчислювальних технологіях (адже ці два поняття визначають саму суть того, в чому полягає програмування), і що OO вдалося розмити майже повністю, тому що в ОО єдиною аксіомою є те, що "все є об’єкт ". І це, як наслідок, багато програмістів в наші дні, як правило, не розуміють / оцінюють / не визнають, тому що їм промили мозок виключно "мислячи шлях OO". Часто ведучи до змінних / змінних об'єктів, що використовуються, як і скрізь , коли значення / незмінні об'єкти могли / часто були б кращими.
val означає незмінний, а var означає змінний
ви можете мислити val
як final
ключовий світ мови програмування java або світ ключових мов c ++ const
。
Val
означає його остаточний , не може бути перепризначений
Тоді як Var
можна перепризначити пізніше .
Це так просто, як його називають.
var означає, що вона може змінюватися
val означає незмінний
Значення Val - введені константи зберігання. Після створення його значення не можна перепризначити. нове значення можна визначити за допомогою ключового слова val.
напр. val x: Int = 5
Тут тип є необов’язковим, оскільки scala може визначити його із призначеного значення.
Var - змінні - це типові одиниці зберігання, яким можна знову присвоїти значення, доки зарезервовано простір пам'яті.
напр. var x: Int = 5
Дані, що зберігаються в обох блоках зберігання, автоматично розподіляються JVM після того, як вони більше не потрібні.
У масштабі масштабів перевагу надають перевагам змінним через стабільність, що приводить до коду, особливо у одночасному та багатопотоковому коді.
Хоча багато хто вже відповіли на різницю між Вал та вар . Але слід зазначити, що val не зовсім як фінал ключове слово.
Ми можемо змінити значення val за допомогою рекурсії, але ми ніколи не можемо змінити значення остаточного. Фінал більш постійний, ніж Вал.
def factorial(num: Int): Int = {
if(num == 0) 1
else factorial(num - 1) * num
}
Параметри методу за замовчуванням знаходяться за вартістю і при кожному значенні виклику змінюється.
var qr = q
робить копіюq
?