Scala में var और val की परिभाषा में क्या अंतर है?


301

स्काला में परिभाषा varऔर valपरिभाषा के बीच क्या अंतर है और भाषा को दोनों की आवश्यकता क्यों है? आप एक valसे अधिक varऔर इसके विपरीत क्यों चुनेंगे ?

जवाबों:


331

जैसा कि कई अन्य लोगों ने कहा है, किसी को सौंपी गई वस्तु को 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। स्वाभाविक रूप से, मैं अन्य उद्देश्यों के लिए चर का पुन: उपयोग करने से बचता था, और इस फ़ंक्शन के बाहर कोई भी कोड उन्हें नहीं देखता है, इसलिए मुझे उनके मूल्यों के बारे में एक पंक्ति से दूसरी पंक्ति में बदलने के बारे में चिंता करने की आवश्यकता नहीं है - जब मैं स्पष्ट रूप से ऐसा करता हूं।

स्काला ने प्रोग्रामर को ऐसा करने का विकल्प चुना, अगर प्रोग्रामर ने इसे सबसे अच्छा समाधान माना। अन्य भाषाओं ने इस तरह के कोड को मुश्किल बनाने के लिए चुना है। मूल्य स्केला (और व्यापक उत्परिवर्तन के साथ कोई भी भाषा) भुगतान करता है कि संकलक के पास कोड को अनुकूलित करने में उतना अधिक मार्ग नहीं है जितना कि अन्यथा हो सकता है। जावा का उत्तर, रन-टाइम प्रोफ़ाइल के आधार पर कोड का अनुकूलन है। हम प्रत्येक पक्ष के पेशेवरों और विपक्षों के बारे में और आगे बढ़ सकते हैं।

निजी तौर पर, मुझे लगता है कि स्काला अभी के लिए सही संतुलन बनाता है। यह सही नहीं है, दूर तक। मुझे लगता है कि स्काला द्वारा क्लोजर और हास्केल दोनों को बहुत दिलचस्प धारणाएं नहीं अपनाई गई हैं, लेकिन स्काला की अपनी ताकत भी है। हम देखेंगे कि भविष्य क्या है।


थोड़ी देर, लेकिन ... की var qr = qएक प्रति बनाता है q?

5
@davips यह संदर्भित ऑब्जेक्ट की एक प्रतिलिपि नहीं बनाता है 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/… )।
ओवेन

@ हाँ, लेकिन ध्यान दें कि यह एक उथली वस्तु है। कोड अभी भी O (n) है कि क्या परिवर्तनशील है, यदि आप कतार की नकल करते हैं, या अपरिवर्तनीय।
डैनियल सी। सोबरल

61

valअंतिम है, जो सेट नहीं किया जा सकता है। finalजावा में सोचो ।


3
लेकिन अगर मैं सही ढंग से (एक स्काला विशेषज्ञ नहीं) समझता हूं, तो val चर अपरिवर्तनीय हैं, लेकिन वे जिन वस्तुओं को संदर्भित करते हैं, वे नहीं होने चाहिए। लिंक के अनुसार स्टीफ़न ने पोस्ट किया: "यहां नामों के संदर्भ को एक अलग सरणी में इंगित करने के लिए नहीं बदला जा सकता है, लेकिन सरणी को संशोधित किया जा सकता है। दूसरे शब्दों में सरणी के सामग्री / तत्वों को संशोधित किया जा सकता है।" तो यह है कि finalजावा में कैसे काम करता है।
डैनियल प्रीडेन

3
वास्तव में क्यों मैं के रूप में यह पोस्ट किया है। मैं +=एक valठीक है के रूप में परिभाषित एक उत्परिवर्तित हैशमैप पर कॉल कर सकता हूं -मेरा मानना ​​है कि finalजावा में इसका वास्तव में कैसे काम होता है
जैक्सन डेविस

Ack, मुझे लगा कि अंतर्निहित स्केला प्रकार केवल पुन: असाइनमेंट की अनुमति देने से बेहतर कर सकता है। मुझे तथ्य-जांच करने की आवश्यकता है।
स्टीफन केंडल

मैं सामान्य धारणा के साथ स्केला के अपरिवर्तनीय अनुक्रम प्रकारों को भ्रमित कर रहा था। कार्यात्मक प्रोग्रामिंग ने मुझे चारों ओर मोड़ दिया है।
स्टीफन केंडल

मैंने आपके उत्तर में एक डमी चरित्र जोड़ा और हटा दिया ताकि मैं आपको उत्थान दे सकूं।
स्टीफन केंडल


20

अंतर यह है कि एक 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.

यदि आप एक डेटा संरचना का निर्माण कर रहे हैं और इसके सभी क्षेत्र vals हैं, तो वह डेटा संरचना इसलिए अपरिवर्तनीय है, क्योंकि इसकी स्थिति परिवर्तित नहीं हो सकती है।


1
यह तभी सही है जब उन क्षेत्रों के वर्ग भी अपरिवर्तनीय हों।
डैनियल सी। सोबरल

हाँ - मैं डाल करने जा रहा था, लेकिन मुझे लगा कि यह एक कदम बहुत दूर हो सकता है! यह भी एक तर्कपूर्ण बात है जो मैं कहूंगा; एक नजरिए से (यद्यपि कोई क्रियाशील नहीं) इसका राज्य अपने राज्य की स्थिति में परिवर्तन नहीं करता है।
oxbow_lakes

एक जेवीएम भाषा में एक अपरिवर्तनीय वस्तु बनाना अभी भी इतना कठिन क्यों है? इसके अलावा, स्काला ने वस्तुओं को डिफ़ॉल्ट रूप से अपरिवर्तनीय क्यों नहीं बनाया?
डेरेक महार

19

valअपरिवर्तनीय का varमतलब है और परस्पर का मतलब है।

पूरी चर्चा।


1
यह बिल्कुल सच नहीं है। जुड़ा हुआ लेख एक परिवर्तनशील सरणी देता है और इसे अपरिवर्तनीय कहता है। कोई गंभीर स्रोत नहीं।
क्लॉस शुल्ज

वास्तव में सच नहीं है। कि वैल ख = सरणी [इंट] (1,2,3) ख (0) = 4 println (b.mkString ( "")) println ( "") की कोशिश करो
Guillaume

13

C ++ के संदर्भ में सोच,

val x: T

गैर-स्थिर डेटा के लिए निरंतर सूचक के अनुरूप है

T* const x;

जबकि

var x: T 

गैर-निरंतर डेटा के लिए गैर-स्थिर सूचक के अनुरूप है

T* x;

अनुकूलता valअधिक varहोने से कोडबेस की अपरिहार्यता बढ़ जाती है जिससे इसकी शुद्धता, सुगमता और समझने की सुविधा हो सकती है।

गैर-स्थिर डेटा के लिए निरंतर सूचक होने के अर्थ को समझने के लिए निम्नलिखित स्काला स्निपेट पर विचार करें:

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)

1
मुझे लगता है कि स्काला ने अपने अंतिम निष्कर्ष के लिए अपरिवर्तनीयता नहीं ली: निरंतर सूचक और निरंतर डेटा। स्काला डिफ़ॉल्ट रूप से वस्तुओं को अपरिवर्तनीय बनाने का एक अवसर चूक गया। नतीजतन, स्काला में वैसी ही धारणा नहीं है जैसी हास्केल की है।
डेरेक महार

@DerekMahar आप सही हैं, लेकिन एक वस्तु स्वयं को पूरी तरह से अपरिवर्तनीय रूप में प्रस्तुत कर सकती है, जबकि इसके कार्यान्वयन में परिवर्तनशीलता का उपयोग करते हुए, जैसे प्रदर्शन के कारणों के लिए। कंपाइलर सत्य म्यूटेबिलिटी और इंटरनल-केवल म्यूटेबिलिटी के बीच कैसे अलग हो पाएगा?
फ्रेंकोइस्र

8

"वैल का मतलब अपरिवर्तनीय होता है और var का अर्थ होता है।"

पैराप्रेज़ के लिए, "वैल का अर्थ है मान और संस्करण का मतलब है चर"।

एक ऐसा अंतर जो कंप्यूटिंग में बेहद महत्वपूर्ण होता है (क्योंकि वे दो अवधारणाएं बहुत सार बताती हैं कि प्रोग्रामिंग सभी के बारे में क्या है), और यह कि ओओ लगभग पूरी तरह से धुंधला करने में कामयाब रहा है, क्योंकि ओओ में, केवल स्वयंसिद्ध है कि "सब कुछ एक है" वस्तु "। और इसके परिणामस्वरूप, इन दिनों बहुत सारे प्रोग्रामर समझ / सराहना / पहचान नहीं करते हैं, क्योंकि उन्हें विशेष रूप से "ओओ रास्ता सोचने" में दिमाग लगाया गया है। अक्सर चर / उत्परिवर्तित वस्तुओं का उपयोग हर जगह की तरह किया जाता है , जब मूल्य / अपरिवर्तनीय वस्तुएं अक्सर बेहतर होती हैं।


यही कारण है कि मैं जावा पर हास्केल पसंद करता हूं, उदाहरण के लिए।
डेरेक महार

6

वैल का मतलब अपरिवर्तनीय होता है और var का मतलब होता है

आप सोच सकते हैं valजावा प्रोग्रामिंग भाषा के रूप में finalकुंजी दुनिया या सी ++ भाषा constकुंजी दुनिया।


1

Valइसका अर्थ है, इसका अंतिम , पुन: असाइन नहीं किया जा सकता

जबकि, बाद में पुन: असाइनVar किया जा सकता है ।


1
यह उत्तर पहले से सबमिट किए गए 12 उत्तरों से अलग कैसे है?
jwvh

0

यह नाम के रूप में सरल है।

var का मतलब यह अलग-अलग हो सकता है

वैल का मतलब होता है अमूर्त


0

वैल - मान टाइप किए गए संग्रहण स्थिरांक हैं। एक बार जब इसका मूल्य बनाया जाता है तो उसे फिर से सौंपा जा सकता है। कीवर्ड वैल के साथ एक नए मान को परिभाषित किया जा सकता है।

जैसे। वैल एक्स: इंट = 5

यहां टाइप वैकल्पिक है क्योंकि स्कैला इसे दिए गए मूल्य से अनुमान लगा सकता है।

Var - वैरिएबल टाइप की गई स्टोरेज इकाइयाँ होती हैं जिन्हें तब तक मूल्यों को सौंपा जा सकता है जब तक कि मेमोरी स्पेस आरक्षित न हो जाए।

जैसे। var x: Int = 5

दोनों भंडारण इकाइयों में संग्रहीत डेटा स्वचालित रूप से JVM द्वारा डी-आबंटित कर दिया जाता है, क्योंकि अब इनकी आवश्यकता नहीं है।

स्केला में मान वैरिएबल पर पसंद किए जाते हैं क्योंकि स्थिरता के कारण ये कोड को विशेष रूप से समवर्ती और मल्टीथ्रेड कोड में लाता है।


0

हालांकि कई पहले ही वैल और वर् के बीच अंतर का जवाब दे चुके हैं । लेकिन ध्यान देने वाली बात यह है कि वैल अंतिम कीवर्ड की तरह नहीं है

हम पुनरावर्तन का उपयोग करके वैल का मान बदल सकते हैं लेकिन हम कभी भी फाइनल का मान नहीं बदल सकते हैं। फाइनल वैल की तुलना में अधिक स्थिर है।

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

विधि पैरामीटर डिफ़ॉल्ट वैल द्वारा हैं और हर कॉल वैल्यू में बदला जा रहा है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.