Apa perbedaan antara a var
dan val
definisi dalam Scala dan mengapa bahasa membutuhkan keduanya? Mengapa Anda memilih val
lebih dari satu var
dan sebaliknya?
Apa perbedaan antara a var
dan val
definisi dalam Scala dan mengapa bahasa membutuhkan keduanya? Mengapa Anda memilih val
lebih dari satu var
dan sebaliknya?
Jawaban:
Seperti yang dikatakan oleh banyak orang lain, objek yang ditugaskan untuk val
tidak dapat diganti, dan objek yang ditugaskan ke var
kaleng. Namun, objek tersebut dapat memiliki keadaan internal yang dimodifikasi. Sebagai contoh:
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.
}
}
Jadi, meskipun kita tidak bisa mengubah objek yang ditugaskan x
, kita bisa mengubah keadaan objek itu. Pada akar itu, bagaimanapun, ada a var
.
Sekarang, kekekalan adalah hal yang baik karena berbagai alasan. Pertama, jika suatu objek tidak mengubah keadaan internal, Anda tidak perlu khawatir jika ada bagian lain dari kode Anda yang mengubahnya. Sebagai contoh:
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")
Ini menjadi sangat penting dengan sistem multithreaded. Dalam sistem multithreaded, hal berikut dapat terjadi:
x = new B(1)
f(x)
if (x.value.value == 1) {
print(x.value.value) // Can be different than 1!
}
Jika Anda menggunakan val
secara eksklusif, dan hanya menggunakan struktur data yang tidak dapat diubah (yaitu, hindari array, semuanya masuk scala.collection.mutable
, dll.), Anda dapat yakin bahwa ini tidak akan terjadi. Yaitu, kecuali ada beberapa kode, mungkin bahkan kerangka kerja, melakukan trik refleksi - sayangnya, refleksi dapat mengubah nilai-nilai yang "tidak berubah".
Itu satu alasan, tetapi ada alasan lain untuk itu. Saat Anda menggunakan var
, Anda dapat tergoda untuk menggunakan kembali yang sama var
untuk berbagai tujuan. Ini memiliki beberapa masalah:
Sederhananya, menggunakan val
lebih aman dan mengarah ke kode yang lebih mudah dibaca.
Maka, kita bisa pergi ke arah lain. Jika val
itu lebih baik, mengapa harus var
sama sekali? Ya, beberapa bahasa memang mengambil rute itu, tetapi ada banyak situasi di mana sifat berubah-ubah meningkatkan kinerja.
Sebagai contoh, ambil yang abadi Queue
. Ketika Anda baik enqueue
atau dequeue
sesuatu di dalamnya, Anda mendapatkan Queue
objek baru . Lalu bagaimana, apakah Anda akan memproses semua item di dalamnya?
Saya akan membahasnya dengan sebuah contoh. Katakanlah Anda memiliki antrian angka, dan Anda ingin membuat angka dari mereka. Misalnya, jika saya memiliki antrian dengan 2, 1, 3, dalam urutan itu, saya ingin mendapatkan kembali nomor 213. Mari kita selesaikan dulu dengan mutable.Queue
:
def toNum(q: scala.collection.mutable.Queue[Int]) = {
var num = 0
while (!q.isEmpty) {
num *= 10
num += q.dequeue
}
num
}
Kode ini cepat dan mudah dimengerti. Kelemahan utamanya adalah bahwa antrian yang dilewati dimodifikasi oleh toNum
, jadi Anda harus membuat salinannya terlebih dahulu. Itulah jenis manajemen objek yang membuat Anda bebas dari kekekalan.
Sekarang, mari kita tutupi dengan 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)
}
Karena saya tidak dapat menggunakan kembali beberapa variabel untuk melacak saya num
, seperti pada contoh sebelumnya, saya perlu menggunakan rekursi. Dalam hal ini, ini adalah rekursi ekor, yang memiliki kinerja yang cukup bagus. Tapi itu tidak selalu terjadi: kadang-kadang tidak ada solusi rekursi ekor yang baik (mudah dibaca, sederhana).
Namun, perhatikan bahwa saya dapat menulis ulang kode itu untuk menggunakan immutable.Queue
dan var
sekaligus! Sebagai contoh:
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
}
Kode ini masih efisien, tidak memerlukan rekursi, dan Anda tidak perlu khawatir apakah Anda harus membuat salinan antrian Anda atau tidak sebelum menelepon toNum
. Secara alami, saya menghindari menggunakan kembali variabel untuk tujuan lain, dan tidak ada kode di luar fungsi ini yang melihatnya, jadi saya tidak perlu khawatir tentang nilai-nilai mereka berubah dari satu baris ke yang berikutnya - kecuali ketika saya secara eksplisit melakukannya.
Scala memilih untuk membiarkan programmer melakukan itu, jika programmer menganggapnya sebagai solusi terbaik. Bahasa lain telah memilih untuk membuat kode seperti itu sulit. Harga yang dibayar Scala (dan bahasa apa pun dengan mutabilitas luas) adalah bahwa kompiler tidak memiliki banyak kelonggaran dalam mengoptimalkan kode sebagaimana seharusnya. Jawaban Java untuk itu adalah mengoptimalkan kode berdasarkan pada profil run-time. Kita bisa terus berbicara tentang pro dan kontra untuk masing-masing pihak.
Secara pribadi, saya pikir Scala menemukan keseimbangan yang tepat, untuk saat ini. Sejauh ini tidak sempurna. Saya pikir Clojure dan Haskell memiliki gagasan yang sangat menarik yang tidak diadopsi oleh Scala, tetapi Scala memiliki kekuatannya sendiri juga. Kita akan melihat apa yang muncul di masa depan.
q
. Itu memang membuat salinan - di stack, bukan heap - dari referensi ke objek itu. Adapun kinerja, Anda harus lebih jelas tentang apa "itu" yang Anda bicarakan.
(x::xs).drop(1)
tepatnya xs
, bukan "salinan" dari xs
) tautan di sini yang dapat saya mengerti. tnx!
qr
merupakan antrian yang tidak dapat diubah, setiap kali ekspresi qr.dequeue
dipanggil, ia membuat new Queue
(lihat < github.com/scala/scala/blob/2.13.x/src/library/scala/collection/… ).
val
bersifat final, yaitu, tidak dapat diatur. Pikirkan final
dalam java.
val
variabel tidak berubah, tetapi objek yang dirujuk tidak harus. Menurut tautan yang diposting Stefan: "Di sini referensi nama tidak dapat diubah untuk mengarah ke Array yang berbeda, tetapi array itu sendiri dapat dimodifikasi. Dengan kata lain isi / elemen array dapat dimodifikasi." Jadi seperti bagaimana cara final
kerjanya di Jawa.
+=
pada hashmap mutabled didefinisikan sebagai val
baik-baik saja-saya percaya persis bagaimana final
bekerja di java
Secara sederhana:
var = var iable
val = v ariable + fin al
Perbedaannya adalah bahwa a var
dapat ditugaskan kembali sedangkan yang val
tidak bisa. Mutabilitas, atau selain dari apa pun yang sebenarnya ditugaskan, adalah masalah sampingan:
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.
Sedangkan:
val n = immutable.Set("London", "Paris")
n = immutable.Set("New York") //Will not compile as n is a val.
Dan karenanya:
val n = mutable.Set("London", "Paris")
n = mutable.Set("New York") //Will not compile, even though the type of n is mutable.
Jika Anda sedang membangun struktur data dan semua bidangnya adalah val
s, maka struktur data itu tidak dapat diubah, karena statusnya tidak dapat berubah.
val
berarti kekal dan var
bisa berubah.
Berpikir dalam hal C ++,
val x: T
analog dengan pointer konstan ke data tidak konstan
T* const x;
sementara
var x: T
analog dengan penunjuk tidak konstan ke data tidak konstan
T* x;
Mendukung val
lebih var
meningkatkan immutabilitas basis kode yang dapat memfasilitasi kebenaran, konkurensi, dan pemahaman.
Untuk memahami arti memiliki pointer konstan ke data non-konstan pertimbangkan cuplikan Scala berikut:
val m = scala.collection.mutable.Map(1 -> "picard")
m // res0: scala.collection.mutable.Map[Int,String] = HashMap(1 -> picard)
Di sini "pointer" val m
konstan sehingga kita tidak dapat menetapkan ulang untuk menunjuk ke hal lain seperti itu
m = n // error: reassignment to val
namun kita memang dapat mengubah data yang tidak konstan itu sendiri yang m
menunjuk seperti itu
m.put(2, "worf")
m // res1: scala.collection.mutable.Map[Int,String] = HashMap(1 -> picard, 2 -> worf)
"val berarti tidak berubah dan var berarti bisa berubah."
Mengutip, "val berarti nilai dan var berarti variabel".
Perbedaan yang terjadi menjadi sangat penting dalam komputasi (karena kedua konsep tersebut mendefinisikan esensi dari semua pemrograman itu), dan bahwa OO telah berhasil mengaburkan hampir sepenuhnya, karena dalam OO, satu-satunya aksioma adalah bahwa "semuanya adalah obyek". Dan sebagai konsekuensinya, banyak programmer saat ini cenderung tidak memahami / menghargai / mengenali, karena mereka telah dicuci otak untuk "berpikir dengan cara OO" secara eksklusif. Seringkali mengarah ke objek variabel / bisa berubah digunakan seperti di mana - mana , ketika nilai / objek berubah mungkin / akan sering lebih baik.
val berarti tidak berubah dan var berarti bisa berubah
Anda dapat berpikir val
sebagai final
dunia kunci bahasa pemrograman java atau c ++ bahasa const
kunci dunia。
Val
berarti final , tidak dapat dipindahkan
Padahal, Var
bisa dipindahkan nanti .
Ini sesederhana namanya.
var berarti dapat bervariasi
val berarti tidak berubah
Nilai - nilai diketikkan sebagai konstanta penyimpanan. Setelah dibuat nilainya tidak dapat ditugaskan kembali. nilai baru dapat didefinisikan dengan kata kunci val.
misalnya. val x: Int = 5
Di sini tipe adalah opsional karena scala dapat mengambilnya dari nilai yang diberikan.
Var - variabel adalah unit penyimpanan yang diketik yang dapat diberi nilai lagi selama ruang memori dicadangkan.
misalnya. var x: Int = 5
Data yang disimpan di kedua unit penyimpanan secara otomatis dialokasikan oleh JVM setelah ini tidak lagi diperlukan.
Dalam nilai scala lebih disukai daripada variabel karena stabilitas ini membawa ke kode terutama dalam kode bersamaan dan multithreaded.
Padahal banyak yang sudah menjawab perbedaan antara Val dan var . Tetapi satu hal yang perlu diperhatikan adalah bahwa val tidak persis seperti kata kunci akhir .
Kita dapat mengubah nilai val menggunakan rekursi tetapi kita tidak pernah bisa mengubah nilai akhir. Final lebih konstan daripada Val.
def factorial(num: Int): Int = {
if(num == 0) 1
else factorial(num - 1) * num
}
Parameter metode secara default val dan pada setiap nilai panggilan sedang diubah.
var qr = q
membuat salinannyaq
?