อะไรคือความแตกต่างระหว่าง 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
และ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
รหัสนี้ยังคงมีประสิทธิภาพไม่จำเป็นต้องเรียกซ้ำและคุณไม่จำเป็นต้องกังวลว่าคุณจะต้องทำสำเนาของคิวของคุณหรือไม่ก่อนที่จะเรียก โดยธรรมชาติแล้วฉันหลีกเลี่ยงการใช้ตัวแปรซ้ำเพื่อวัตถุประสงค์อื่นและไม่มีโค้ดใดนอกฟังก์ชั่นนี้ที่เห็นพวกเขาดังนั้นฉันไม่ต้องกังวลเกี่ยวกับการเปลี่ยนแปลงค่าของพวกเขาจากบรรทัดหนึ่งไปเป็นบรรทัดถัดไป - ยกเว้นเมื่อฉันทำอย่างชัดเจน
สกาล่าเลือกที่จะให้โปรแกรมเมอร์ทำเช่นนั้นหากโปรแกรมเมอร์เห็นว่าเป็นทางออกที่ดีที่สุด ภาษาอื่น ๆ ได้เลือกที่จะทำให้รหัสนั้นยาก ราคาสกาล่า (และภาษาใด ๆ ที่มีความไม่แน่นอนอย่างแพร่หลาย) จ่ายคือคอมไพเลอร์ไม่มีวิธีการเพิ่มประสิทธิภาพโค้ดมากเท่าที่ควร คำตอบของจาวาคือการปรับรหัสให้เหมาะสมตามโปรไฟล์ของเวลาทำงาน เราสามารถดำเนินต่อไปเรื่อย ๆ เกี่ยวกับข้อดีข้อเสียของแต่ละด้าน
โดยส่วนตัวแล้วฉันคิดว่าสกาล่าจะทำให้เกิดสมดุลในตอนนี้ มันไม่สมบูรณ์แบบเลยทีเดียว ฉันคิดว่าทั้งClojureและHaskellมีแนวคิดที่น่าสนใจที่ไม่ได้รับการรับรองจาก Scala แต่ Scala มีจุดแข็งของตัวเองเช่นกัน เราจะเห็นสิ่งที่เกิดขึ้นในอนาคต
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
ตัวแปรจะไม่เปลี่ยนรูป แต่วัตถุที่พวกเขาอ้างอิงไม่จำเป็นต้องเป็น อ้างอิงจากลิงค์ Stefan ที่โพสต์: "ที่นี่การอ้างอิงชื่อไม่สามารถเปลี่ยนเป็นชี้ไปที่ Array อื่นได้ แต่อาเรย์นั้นสามารถแก้ไขได้เองในคำอื่น ๆ เนื้อหา / องค์ประกอบของอาเรย์สามารถแก้ไขได้" ดังนั้นจึงเป็นเหมือนวิธีการfinal
ทำงานใน Java
+=
ใน HashMap mutabled กำหนดให้เป็น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
การเพิ่มขึ้นของการไม่เปลี่ยนรูปของ codebase ที่สามารถอำนวยความสะดวกในความถูกต้องของการทำงานพร้อมกันและทำความเข้าใจได้
เพื่อทำความเข้าใจความหมายของการมีตัวชี้ค่าคงที่ไปยังข้อมูลที่ไม่คงที่ให้พิจารณาข้อมูลโค้ด 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
โลกแห่งการเขียนโปรแกรมภาษาจาวาหรือ c ++ ภาษาconst
หลักโลก。
Val
หมายถึงขั้นสุดท้ายไม่สามารถกำหนดใหม่ได้
ในขณะที่Var
สามารถมีพระราชเสาวนีย์ในภายหลัง
มันง่ายเหมือนชื่อ
var หมายความว่าสามารถเปลี่ยนแปลงได้
วาลหมายถึงคงที่
Val - ค่าเป็นค่าคงที่ของการจัดเก็บข้อมูลที่พิมพ์ เมื่อสร้างมูลค่าแล้วจะไม่สามารถกำหนดใหม่ได้ ค่าใหม่สามารถกำหนดได้ด้วยคีย์เวิร์ด val
เช่น. val x: Int = 5
ประเภทนี้เป็นทางเลือกเนื่องจากสกาล่าสามารถอนุมานได้จากค่าที่กำหนด
ตัวแปร - คือหน่วยเก็บข้อมูลที่พิมพ์ซึ่งสามารถกำหนดค่าได้อีกครั้งตราบใดที่พื้นที่หน่วยความจำถูกสงวนไว้
เช่น. var x: Int = 5
ข้อมูลที่เก็บไว้ในทั้งสองหน่วยเก็บข้อมูลจะถูกยกเลิกการจัดสรรโดย JVM โดยอัตโนมัติเมื่อไม่ต้องการใช้อีกต่อไป
ในค่าสกาล่าเป็นที่ต้องการมากกว่าตัวแปรเนื่องจากความมั่นคงเหล่านี้นำมาสู่รหัสโดยเฉพาะอย่างยิ่งในรหัสพร้อมกันและแบบมัลติเธรด
แม้ว่าหลายคนจะตอบความแตกต่างระหว่างValและvarแล้ว แต่สิ่งหนึ่งที่สังเกตได้คือval นั้นไม่เหมือนกับคำค้นหาสุดท้าย
เราสามารถเปลี่ยนค่าของ val โดยใช้การเรียกซ้ำ แต่เราไม่สามารถเปลี่ยนค่าสุดท้ายได้ สุดท้ายคือค่าคงที่มากกว่า Val
def factorial(num: Int): Int = {
if(num == 0) 1
else factorial(num - 1) * num
}
พารามิเตอร์ของเมธอดจะเป็นค่าเริ่มต้นและทุกค่าการโทรจะถูกเปลี่ยน
var qr = q
ทำสำเนาq
ไหม?