Scala'da bir var ve val tanımı arasındaki fark nedir?


301

Scala'da a varve valtanım arasındaki fark nedir ve neden dilin ikisine de ihtiyacı vardır? Neden valbir varve daha fazlasının seçimini yapasınız ki?

Yanıtlar:


331

Diğerlerinin söylediği gibi, bir nesneye atanan nesne valdeğiştirilemez ve bir kutuya atanan nesne değiştirilemez var. Ancak, söz konusu nesnenin dahili durumu değiştirilebilir. Örneğin:

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

Dolayısıyla, atanan nesneyi xdeğiştiremesek de, o nesnenin durumunu değiştirebiliriz. Bununla birlikte, kökünde bir var.

Şimdi, değişmezlik birçok nedenden dolayı iyi bir şeydir. İlk olarak, bir nesne dahili durumu değiştirmezse, kodunuzun başka bir kısmı onu değiştiriyorsa endişelenmenize gerek yoktur. Örneğin:

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

Bu, çok iş parçacıklı sistemlerde özellikle önemli hale gelir. Çok iş parçacıklı bir sistemde aşağıdakiler olabilir:

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

Eğer kullanırsanız valmünhasıran ve sadece iletmenin veri yapıları (yani, önlemek diziler, her şeyi kullanmak scala.collection.mutablevb), bu olmayacak emin olabilirsiniz. Yani, bazı kodlar, belki de bir çerçeve bile, yansıma hileleri yapmadıkça - yansıma ne yazık ki "değişmez" değerleri değiştirebilir.

Bu bir sebep, ama bunun başka bir nedeni daha var. Kullandığınızda var, bunu varbirden fazla amaç için yeniden kullanmaya cazip gelebilirsiniz . Bunun bazı sorunları var:

  • Kodu okuyan insanlar için, kodun belirli bir bölümündeki bir değişkenin değerinin ne olduğunu bilmek daha zor olacaktır.
  • Değişkeni bazı kod yollarında yeniden başlatmayı ve kodda aşağı yönde yanlış değerler iletmeyi unutabilirsiniz.

Basitçe söylemek gerekirse, kullanmak valdaha güvenlidir ve daha okunabilir koda yol açar.

O zaman diğer yöne gidebiliriz. Bu valdaha iyi ise, neden varhiç var ? Bazı diller bu rotayı izledi, ancak değişebilirliğin performansı arttırdığı durumlar var.

Örneğin, değişmez bir şey alın Queue. İçinde ya enqueueda dequeueiçinde bir şey olduğunda, yeni bir Queuenesne alırsın . O zaman, içindeki tüm öğeleri işlemeye ne dersiniz?

Bunu bir örnekle ele alacağım. Diyelim ki bir rakam kuyruğunuz var ve bunlardan bir sayı oluşturmak istiyorsunuz. Örneğin, bu sırayla 2, 1, 3 ile bir kuyruğum varsa, 213 sayısını geri almak istiyorum. Önce bir ile çözelim mutable.Queue:

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

Bu kod hızlı ve anlaşılması kolaydır. Ana dezavantajı, geçen kuyruğun tarafından değiştirilmesidir toNum, bu nedenle önceden bir kopyasını oluşturmanız gerekir. Bu, değişmezliğin sizi özgür bıraktığı bir tür nesne yönetimi.

Şimdi bunu bir şuna çevirelim 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Önceki örnekte olduğu gibi, bazı değişkenleri takip etmek için tekrar kullanamadığım için özyinelemeye başvurmam gerekiyor. Bu durumda, oldukça iyi bir performansa sahip bir kuyruk özyineleme. Ancak bu her zaman böyle değildir: bazen sadece iyi (okunabilir, basit) kuyruk özyineleme çözümü yoktur.

Ancak, bir immutable.Queueve varaynı anda kullanmak için bu kodu yeniden yazabilirsiniz unutmayın ! Örneğin:

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
}

Bu kod hala etkilidir, özyineleme gerektirmez ve aramadan önce sıranızın bir kopyasını oluşturup oluşturmama konusunda endişelenmenize gerek yoktur toNum. Doğal olarak, değişkenleri başka amaçlarla yeniden kullanmaktan kaçındım ve bu fonksiyonun dışındaki hiçbir kod onları görmüyor, bu yüzden değerleri açıkça bir satırdan diğerine değişme konusunda endişelenmem gerekmiyor.

Scala, programcı en iyi çözüm olduğunu düşünürse, programcının bunu yapmasına izin vermeyi tercih etti. Diğer diller bu kodu zorlaştırmayı seçmiştir. Scala'nın (ve yaygın olarak değiştirilebilen herhangi bir dilin) ​​ödediği fiyat, derleyicinin kodu başka türlü olabildiğince optimize etmede çok fazla boşluğa sahip olmamasıdır. Java'nın cevabı, kodu çalışma zamanı profiline göre optimize etmektir. Her iki tarafın artıları ve eksileri hakkında devam edebiliriz.

Şahsen, Scala şimdilik doğru dengeye ulaşıyor. Şimdiye kadar mükemmel değil. Her iki düşünüyorum Clojure ve Haskell Scala tarafından benimsenen değil çok ilginç fikirleri vardır, ancak Scala da kendi güçlü yanları vardır. Gelecekte ne olacağını göreceğiz.


Biraz geç ama ... ... var qr = qkopyasını çıkarıyor mu q?

5
@davips Başvurulan nesnenin bir kopyasını oluşturmaz q. Bu nesneye yapılan başvurunun yığınını değil yığınını kopyalar . Performansa gelince, neden bahsettiğiniz "o" hakkında daha net olmanız gerekir.
Daniel C.Sobral

Tamam, senin yardımın ve bazı bilgiler ile ( (x::xs).drop(1)tam olarak xs, bir "kopyası" değil xs) buradan anlayabiliyorum bağlantı . tnx!

"Bu kod hala etkili" - değil mi? Yana qrdeğişmez bir kuyruk olduğunu ifade her zaman qr.dequeuedenir yapar bir new Queue(<bkz github.com/scala/scala/blob/2.13.x/src/library/scala/collection/... ).
Owen

@Owen Evet, ancak sığ bir nesne olduğuna dikkat edin. Kuyruğu kopyalarsanız veya değiştirilemezse kod hala O (n) 'dir.
Daniel C. Sobral

61

valnihai, yani ayarlanamaz. Java'da düşünün final.


3
Ancak doğru bir şekilde anlarsam (Scala uzmanı değil), val değişkenler değişmezdir, ancak referans aldıkları nesnelerin olması gerekmez. Stefan'ın yayınladığı bağlantıya göre: "Burada ad referansı farklı bir Diziyi işaret edecek şekilde değiştirilemez, ancak dizinin kendisi değiştirilebilir. Başka bir deyişle dizinin içeriği / öğeleri değiştirilebilir." Yani finalJava'da nasıl çalışıyor.
Daniel Pryden

3
Tam olarak neden olduğu gibi yayınladım. Ben adil para cezası +=olarak tanımlanan mutabled bir hashmap çağırabilirim val-tam olarak nasıl finaljava çalışır inanıyorum
Jackson Davis

Ack, yerleşik scala türlerinin sadece yeniden atamaya izin vermekten daha iyisini yapabileceğini düşündüm. Gerçekleri kontrol etmem gerek.
Stefan Kendall

Skala'nın değişmez Dizi tiplerini genel kavramla karıştırıyordum. İşlevsel programlama beni tamamen tersine çevirdi.
Stefan Kendall

Cevabınıza bir kukla karakter ekledim ve kaldırdım, böylece size oy verebilirim.
Stefan Kendall

55

Basit bir ifadeyle:

var = var iable

val = v alınabilir + fin al


20

Aradaki fark, vara'nın yeniden atanabilmesi, ancak a'nın atanamamasıdır val. Değişebilirlik ya da gerçekte atanmış olan herhangi bir şey, bir yan konudur:

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.

Buna karşılık:

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

Ve dolayısıyla:

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

Bir veri yapısı oluşturuyorsanız ve tüm alanları vals ise, bu durumda veri yapısı değişmez, çünkü durumu değişemez.


1
Bu sadece bu alanların sınıfları değişmezse de geçerlidir.
Daniel C. Sobral

Evet - Ben koyacağım ama çok ileri bir adım olabileceğini düşündüm! Aynı zamanda söyleyebileceğim tartışmalı bir nokta; bir bakış açısından (işlevsel olmasa da) durumu, durumunun durumu değişse bile değişmez.
oxbow_lakes

JVM dilinde değişmez bir nesne yaratmak neden hala bu kadar zor? Dahası, Scala neden nesneleri varsayılan olarak değiştirilemedi?
Derek Mahar

19

val"Değişmez", değişmez varanlamına gelir.

Tam tartışma.


1
Bu doğru değil. Bağlantılı makale değiştirilebilir bir dizi verir ve bunu değişmez olarak adlandırır. Ciddi bir kaynak yok.
Klaus Schulz

Gerçekten doğru değil. Val b = Dizi [Int] (1,2,3) b (0) = 4 println (b.mkString ("")) println ("")
Guillaume

13

C ++ açısından düşünmek,

val x: T

sabit göstergeden sabit olmayan verilere benzer

T* const x;

süre

var x: T 

sabit olmayan göstergeye sabit olmayan göstergeye benzer

T* x;

Kayırarak valüzerinde varonun doğruluğu, eşzamanlılık ve anlaşılabilirliğini kolaylaştırabilir kod temeli değişmezlik arttırır.

Sabit olmayan verilere sabit bir işaretçi olmanın anlamını anlamak için aşağıdaki Scala snippet'ini göz önünde bulundurun:

val m = scala.collection.mutable.Map(1 -> "picard")
m // res0: scala.collection.mutable.Map[Int,String] = HashMap(1 -> picard)

Burada "işaretçi" val m sabittir, bu yüzden böyle bir şeye işaret etmek için yeniden atayamayız

m = n // error: reassignment to val

ancak buna mbenzeyen sabit olmayan verilerin kendisini gerçekten değiştirebiliriz

m.put(2, "worf")
m // res1: scala.collection.mutable.Map[Int,String] = HashMap(1 -> picard, 2 -> worf)

1
Bence Scala nihai sonucuna göre değişmezlik göstermedi: sürekli işaretçi ve sabit veri Scala, nesneleri varsayılan olarak değiştirilemez hale getirme fırsatını kaçırdı. Sonuç olarak, Scala Haskell ile aynı değer kavramına sahip değildir.
Derek Mahar

@DerekMahar haklısınız, ancak bir nesne, örneğin performans nedenleriyle , uygulamada hala değişebilirliği kullanırken kendisini mükemmel şekilde değiştirilemez olarak sunabilir . Derleyici gerçek değişebilirlik ve yalnızca dahili değişebilirlik arasında nasıl ayrım yapabilir?
francoisr

8

"val değişmez anlamına gelir ve var değişebilir anlamına gelir."

Paraphrase için, "val değer anlamına gelir ve var değişken anlamına gelir".

Hesaplamada son derece önemli olan bir ayrım (çünkü bu iki kavram, programlamanın neyle ilgili olduğunu özünde tanımlar) ve OO neredeyse tamamen bulanıklaşmayı başarmıştır, çünkü OO'da tek aksiyom "her şey bir nesne". Ve sonuç olarak, bu günlerde birçok programcı anlama / takdir etme / tanıma eğiliminde değil, çünkü sadece “OO yolunu düşünmek” için beyinleri yıkandı. Genellikle değişken / değişken nesnelerin her yerde olduğu gibi kullanılmasına yol açar , değer / değişmez nesneler genellikle daha iyi olabilirdi / olurdu.


Örneğin Haskell'i Java yerine tercih etmemin nedeni budur.
Derek Mahar

6

val değişmez anlamına gelir ve var değişebilir anlamına gelir

valjava programlama dili finalanahtar dünyası veya c ++ dil constanahtarı dünyası olarak düşünebilirsiniz 。


1

Valfinal anlamına gelir , olamaz yeniden

Oysa Varedilebilir sonradan yeniden .


1
Bu cevap, halihazırda gönderilen 12 cevaptan nasıl farklı?
jwvh

0

Adı kadar basit.

var değişebileceği anlamına gelir

val değişmez anlamına gelir


0

Val - değerleri yazılan depolama sabitleridir. Oluşturulduktan sonra değeri yeniden atanamaz. val anahtar kelimesi ile yeni bir değer tanımlanabilir.

Örneğin. val x: Int = 5

Burada tip isteğe bağlıdır çünkü scala onu atanan değerden çıkarabilir.

Var - değişkenler, bellek alanı ayrıldığı sürece tekrar değerler atanabilecek tipte depolama birimleridir.

Örneğin. var x: Int = 5

Her iki depolama biriminde saklanan veriler, artık gerekli olmadığında JVM tarafından otomatik olarak ayrılır.

Skala değerlerinde kararlılık nedeniyle değişkenler tercih edilir, bunlar koda özellikle eşzamanlı ve çok iş parçacıklı kodlar getirir.


0

Birçoğu Val ve var arasındaki farkı zaten yanıtladı . Ancak dikkat edilmesi gereken bir nokta, val'in tam olarak nihai anahtar kelime gibi olmadığıdır .

Özyineleme kullanarak val değerini değiştirebiliriz, ancak final değerini asla değiştiremeyiz. Final Val'den daha sabittir.

def factorial(num: Int): Int = {
 if(num == 0) 1
 else factorial(num - 1) * num
}

Yöntem parametreleri varsayılan olarak val'dir ve her çağrı değerinde değiştirilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.