2009-11-24 4 views

ответ

284

Как и многие другие, объект, назначенный на 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 и 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 на это - оптимизация кода на основе профиля времени выполнения. Мы могли бы продолжать и обсуждать все плюсы и минусы каждой стороны.

Лично я считаю, что на данный момент Scala поражает правильный баланс. Это далеко не идеальный вариант. Я думаю, что у Clojure и Haskell есть очень интересные понятия, не принятые Scala, но у Scala есть свои сильные стороны. Посмотрим, что произойдет в будущем.

+0

Немного поздно, но ... Выдает ли 'var qr = q' копию' q'? – 2013-01-14 13:43:43

+0

Как это влияет на производительность (время процессора)? – 2013-01-14 14:21:29

+5

@davips Это не делает копию объекта, на который ссылается 'q'. Он делает копию - в стеке, а не кучу - _reference_ к этому объекту. Что касается производительности, вам нужно будет более четко понять, о чем «это» вы говорите. –

19

val означает непреложный и var означает изменчивый.

Full discussion.

+0

Это просто неправда. Связанная статья дает изменяемый массив и называет его неизменным. Нет серьезного источника. –

+0

Не правда. Попробуйте, чтобы val b = Array [Int] (1,2,3) b (0) = 4 println (b.mkString ("")) println ("") –

50

val является окончательным, то есть, не может быть установлен. Подумайте, final в java.

+3

Но если я правильно понял (не Scala эксперт), переменные 'val' * * неизменяемы, но объекты, которые они ссылаются, не обязательно должны быть. Согласно ссылке, опубликованной Stefan: «Здесь ссылка на имена не может быть изменена, чтобы указать на другой массив, но сам массив можно изменить. Другими словами, содержимое/элементы массива могут быть изменены». Это похоже на то, как «final» работает на Java. –

+3

Именно поэтому я разместил его как есть. Я могу называть '+ =' на мультилированном хэшмапе, определенном как «val», просто отлично, я верю, что он точно как «final» работает в java –

+0

Ack, я думал, что встроенные типы scala могут сделать лучше, назначение. Мне нужно проверить факт. –

19

Разница в том, что 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, тогда структура данных поэтому неизменна, так как ее состояние не может измениться.

+1

Это верно только в том случае, если классы этих полей неизменяемы. –

+0

Да, я собирался вставить это, но я подумал, что это может быть слишком далеко! Это также спорный момент, который я бы сказал; с одной точки зрения (хотя и не функциональной) ее состояние не меняется, даже если состояние его состояния. –

+0

Почему так сложно создать неизменяемый объект на языке JVM? Кроме того, почему Scala не делала объекты неизменяемыми по умолчанию? –

8

"val означает неизменяемый и var означает mutable."

Чтобы перефразировать, значение значения val и var означает переменную ".

Различие, которое чрезвычайно важно при вычислении (поскольку эти две концепции определяют саму суть программирования), и что OO удалось полностью размыть, поскольку в OO единственной аксиомой является то, что «все - объект». И, как следствие, многие программисты в наши дни, как правило, не понимают/ценят/не признают, потому что им «промывают мозги» исключительно «мышлением OO». Часто приводя к переменным/изменяемым объектам, используемым как везде, когда объекты ценности/неизменяемости могли бы/были бы лучше.

+0

Вот почему я предпочитаю Haskell над Java, например. –

6

Вал означает неизменным и вар означает изменчивым

вы можете думать val как язык программирования Java final Клавиша мира или языка C++ const ключевого мир

37

Проще говоря:.

var = var iable

вал = v ariable + отд аль

11

Мышление в терминах C++,

val x: T 

аналогична постоянной указатель на непостоянных данных

T* const x; 

в то время как

var x: T 

аналогичен непостоянный указатель на непостоянные данные

T* x; 

благоприятствующих val над var увеличивает неизменность кодовых которые могут облегчить его правильность, параллелизм и понятность.

+0

Я думаю, что Scala не взяла неизменность до своего окончательного вывода: постоянный указатель и постоянные данные. Scala упустила возможность сделать объекты неизменяемыми по умолчанию. Следовательно, у Scala нет такого же понятия ценности, как Haskell. –

1

Это простое имя.

вар означает, что он может варьироваться

вал означает неизменный

2

вал похож на конечной переменной в Java. После инициализации значение val никогда не может быть переназначено.

A var, напротив, похож на не конечную переменную в Java. A var может быть переназначен на протяжении всей его жизни.

0

Val - значения являются типизированными константами хранения. После создания его значение не может быть повторно назначено.новое значение может быть определено с помощью ключевого слова val.

например. val x: Int = 5

Здесь тип необязательный, поскольку scala может вывести его из назначенного значения.

Var - переменные являются типизированными единицами хранения, которые могут быть назначены значения еще раз, пока зарезервировано пространство памяти.

например. var x: Int = 5

Данные, хранящиеся в обоих единицах хранения, автоматически де-распределяются JVM, если они больше не нужны.

В значениях scala предпочтительнее переменных из-за стабильности, которые приводят к коду, в частности, в параллельном и многопоточном коде.

Смежные вопросы