2009-11-24 36 views

Odpowiedz

284

Tak jak powiedziało wielu innych, obiekt przypisany do val nie może zostać zastąpiony, a obiekt przypisany do var może. Jednak ten obiekt może mieć zmodyfikowany stan wewnętrzny. Na przykład:

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. 
    } 
} 

Tak, chociaż nie możemy zmienić obiekt przypisany do x, moglibyśmy zmienić stan tego obiektu. Jednak u jego źródła był var.

Teraz niezmienność jest dobra z wielu powodów. Po pierwsze, jeśli obiekt nie zmienia stanu wewnętrznego, nie musisz się martwić, jeśli zmienia go inna część kodu. Na przykład:

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") 

Jest to szczególnie ważne w przypadku systemów wielowątkowych.W systemie wielowątkowym, może dojść do następujących:

x = new B(1) 
f(x) 
if (x.value.value == 1) { 
    print(x.value.value) // Can be different than 1! 
} 

Jeśli używasz val wyłącznie i użyć tylko niezmiennych struktur danych (czyli uniknąć tablic, wszystko w scala.collection.mutable, etc.), można mieć pewność, to won nie stało się. To znaczy, jeśli nie ma jakiegoś kodu, może nawet ramy, robiąc triki refleksji - niestety, odbicie może zmienić "niezmienne" wartości.

To jeden z powodów, ale jest jeszcze jeden powód. Podczas korzystania z var możesz ulec pokusie ponownego użycia tego samego var do wielu celów. Ma to pewne problemy:

  • Będzie trudniej ludziom czytającym kod dowiedzieć się, jaka jest wartość zmiennej w określonej części kodu.
  • Możesz zapomnieć o ponownym zainicjalizowaniu zmiennej w ścieżce kodu, a kończy się przekazywaniem błędnych wartości w kanale.

Po prostu, używanie numeru val jest bezpieczniejsze i prowadzi do bardziej czytelnego kodu.

Możemy zatem iść w innym kierunku. Jeśli numer val jest lepszy, dlaczego w ogóle masz var? Niektóre języki brały tę trasę, ale są sytuacje, w których zmienność znacznie poprawia wydajność.

Weźmy na przykład niezmienny Queue. Gdy masz w nim rzeczy enqueue lub dequeue, otrzymujesz nowy obiekt Queue. Jak zatem, czy mógłbyś przetwarzać wszystkie zawarte w nim elementy?

Przejdę przez to z przykładem. Powiedzmy, że masz kolejkę cyfr i chcesz skomponować z nich numer. Na przykład, jeśli mam kolejkę z 2, 1, 3, w tej kolejności, chcę wrócić pierwszy numer 213. Powiedzmy go rozwiązać z mutable.Queue:

def toNum(q: scala.collection.mutable.Queue[Int]) = { 
    var num = 0 
    while (!q.isEmpty) { 
    num *= 10 
    num += q.dequeue 
    } 
    num 
} 

Ten kod jest szybka i łatwa Rozumiesz. Jego główną wadą jest to, że kolejka, która jest przekazywana, jest modyfikowana przez toNum, więc musisz ją wcześniej zrobić. To rodzaj zarządzania obiektami, z którego uniezależnia cię niezmienność.

A teraz przekształca go do 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) 
} 

Bo nie można ponownie wykorzystać pewną zmienną śledzić mojego num, podobnie jak w poprzednim przykładzie, muszę uciekać się do rekursji. W tym przypadku jest to rekurencja ogona, która ma całkiem niezłe wyniki. Ale nie zawsze tak jest: czasami nie ma po prostu dobrego (czytelnego, prostego) rozwiązania rekurencji ogona.

Należy jednak pamiętać, że mogę przepisać ten kod do korzystania z immutable.Queue i var w tym samym czasie! Na przykład:

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 
} 

Kod ten jest nadal sprawny, nie wymaga rekurencji i nie trzeba się martwić, czy trzeba zrobić kopię kolejce czy nie przed wywołaniem toNum. Oczywiście uniknąłem ponownego użycia zmiennych do innych celów i żaden kod spoza tej funkcji ich nie widzi, więc nie muszę się martwić, że ich wartości zmieniają się z jednej linii na drugą - z wyjątkiem sytuacji, gdy ja to wyraźnie robię.

Scala zdecydowała się, aby programista zrobił to, jeśli programista uznał to za najlepsze rozwiązanie. Inne języki postanowiły utrudnić taki kod. Cena Scala (i jakikolwiek język z dużą zmiennością) płaci, że kompilator nie ma takiej możliwości optymalizacji kodu, jak mógłby inaczej. Odpowiedzią Javy na to jest optymalizacja kodu na podstawie profilu wykonawczego. Możemy mówić o zaletach i wadach dla każdej strony.

Osobiście uważam, że Scala osiąga właściwą równowagę, na razie. To zdecydowanie nie jest doskonałe. Myślę, że zarówno Clojure, jak i Haskell mają bardzo interesujące pojęcia nie przyjęte przez Scalę, ale Scala ma również swoje mocne strony. Zobaczymy, co wyjdzie na przyszłość.

+0

Trochę za późno, ale ... Czy 'var qr = q' tworzy kopię' q'? –

+0

Jak wpływa to na wydajność (czas procesora)? –

+5

@davips Nie tworzy kopii obiektu, do którego odnosi się 'q'. Tworzy kopię - na stosie, a nie stertę - odniesienia do tego obiektu. Jeśli chodzi o wydajność, musisz mieć większą jasność co do "tego", o którym mówisz. –

19

val oznacza niezmienny, a var oznacza zmienny.

Full discussion.

+0

To nie jest prawda. Połączony artykuł podaje tablicę zmiennych i nazywa ją niezmienną. Brak poważnego źródła. –

+0

Rzeczywiście nie prawda. Spróbuj, że val b = Array [Int] (1,2,3) b (0) = 4 println (b.mkString ("")) println ("") –

50

val jest ostateczna, czyli nie może być ustawiony. Think final w języku Java.

+3

Ale jeśli rozumiem poprawnie (nie Scala ekspert), zmienne 'val' * są niezmienne, ale obiekty, do których się odwołują, nie muszą być. Zgodnie z linkiem Stefan pisał: "Tu referencje nazw nie mogą być zmienione tak, aby wskazywały na inną tablicę, ale sama tablica może być modyfikowana, innymi słowy zawartość/elementy tablicy mogą być modyfikowane." To tak, jak "ostatnie" działa w Javie. –

+3

Dokładnie, dlaczego opublikowałem to tak jak jest. Mogę nazywać '+ =' na zmutowanej mapie nieefektywnej zdefiniowanej jako 'val' po prostu dobrze-Wierzę, że dokładnie to jak działa' final' w java –

+0

Ack, myślałem, że wbudowane typy scala mogą zrobić lepiej niż po prostu zadanie. Muszę sprawdzić fakt. –

19

Różnica polega na tym, że var można ponownie przypisać, podczas gdy val nie może. Zmienność, lub w inny sposób bez względu na faktycznie przypisany jest kwestią strona:

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. 

następuje:

val n = immutable.Set("London", "Paris") 
n = immutable.Set("New York") //Will not compile as n is a val. 

I stąd:

val n = mutable.Set("London", "Paris") 
n = mutable.Set("New York") //Will not compile, even though the type of n is mutable. 

Jeśli budujesz strukturę danych i wszystkie jego pola są val s, to struktura danych jest zatem niezmienna, ponieważ jej stan nie może się zmienić.

+1

Jest to prawdą tylko wtedy, gdy klasy tych pól są również niezmienne. –

+0

Tak, chciałem to napisać, ale myślałem, że to o krok za daleko! Jest to również kwestia dyskusyjna, którą chciałbym powiedzieć; z jednej perspektywy (choć nie funkcjonalnej) jej stan się nie zmienia, nawet jeśli ma to miejsce w jego stanie. –

+0

Dlaczego tak trudno jest stworzyć niezmienny obiekt w języku JVM? Co więcej, dlaczego Scala domyślnie nie unieruchamiała obiektów? –

8

"val oznacza niezmienny, a var oznacza zmienny."

Parafrazując "val oznacza wartość, a var oznacza zmienną".

Rozróżnienie, które ma ogromne znaczenie w informatyce (ponieważ te dwie koncepcje określają istotę tego, o co programuje) i że OO udało się zamazać prawie całkowicie, ponieważ w OO jedynym aksjomatem jest to, że "wszystko jest przedmiotem". I w konsekwencji, wielu programistów w dzisiejszych czasach raczej nie rozumie/docenia/nie rozpoznaje, ponieważ zostali poddani praniu mózgu w "myśleniu o drodze" wyłącznie. Często prowadzące do obiektów zmiennych/zmiennych, które są używane jako wszędzie gdzie, gdy wartości/niezmienne obiekty mogłyby/byłyby często lepsze.

+0

To jest powód dla którego wolę Haskella na przykład od Javy. –

6

val oznacza niezmienny i var oznacza zmienny

można myśleć val jako języka programowania Java final klucz światowej lub języka C++ const klucz świat

37

W prostych słowach.

var = var iable

Val = V ariable + żebro in

11

myślenie w zakresie C++,

val x: T 

analogicznie do stałego wskaźnika danych nie stałych

T* const x; 

podczas gdy

var x: T 

analogiczny do niestałej wskaźnika danych nie stałych

T* x; 

Preferowanie val nad var zwiększa niezmienność w kodzie, który może ułatwić jej poprawności, współbieżności i zrozumiałości.

+0

Myślę, że Scala nie uzyskała niezmienności w końcowym wniosku: stała wskazówka i stałe dane. Scala nie wykorzystała okazji do domyślnego ustawienia obiektów niezmiennych. W związku z tym Scala nie ma takiego samego pojęcia wartości jak Haskell. –

1

To tak proste jak imię.

var oznacza, że ​​może ona zmieniać

Val oznacza niezmienny

2

Val jest podobny do końcowego zmiennej Java. Po zainicjowaniu nie można ponownie przypisać numeru val.

A var, dla kontrastu, jest podobna do nieostatniej zmiennej w Javie. Zmienną var można przypisać ponownie przez całe jej życie.

0

Wartości Val są wpisanymi stałami pamięci. Po utworzeniu jego wartość nie może być ponownie przypisana.nową wartość można zdefiniować za pomocą słowa kluczowego val.

np. val x: Int = 5

Tutaj typ jest opcjonalny, ponieważ scala może wywnioskować go z przypisanej wartości.

Var - zmienne są wpisanymi jednostkami pamięci, do których można ponownie przypisać wartości, o ile zarezerwowana jest pamięć.

np. var x: Int = 5

Dane przechowywane w obu jednostkach pamięci są automatycznie usuwane z pamięci przez JVM, gdy nie są już potrzebne.

W wartościach scala są preferowane w stosunku do zmiennych ze względu na stabilność, które przynoszą do kodu, szczególnie w kodzie współbieżnym i wielowątkowym.

Powiązane problemy