स्काला में val-mutable बनाम var-immutable


99

क्या स्केलेबल में कोई दिशा-निर्देश है जब वैल्यू को एक म्यूचुअल संग्रह के साथ उपयोग करने के लिए बनाम एक अपरिवर्तनीय संग्रह के साथ संस्करण का उपयोग करने के लिए? या क्या आपको वास्तव में एक अपरिवर्तनीय संग्रह के साथ घाटी के लिए लक्ष्य करना चाहिए?

तथ्य यह है कि दोनों प्रकार के संग्रह हैं, मुझे बहुत पसंद करते हैं, और अक्सर मुझे नहीं पता कि उस विकल्प को कैसे बनाया जाए।


उदाहरण के लिए देखें stackoverflow.com/questions/10999024/…
लुइगी प्लिंज

जवाबों:


105

बहुत आम सवाल है, यह एक। मुश्किल चीज डुप्लिकेट ढूंढ रही है।

आपको संदर्भित पारदर्शिता के लिए प्रयास करना चाहिए । इसका क्या मतलब है कि, अगर मेरे पास एक अभिव्यक्ति "ई" है, तो मैं एक बना सकता हूं val x = e, और उसके eसाथ बदल सकता हूं x। यह वह संपत्ति है जो उत्परिवर्तन को तोड़ती है। जब भी आपको एक डिजाइन निर्णय लेने की आवश्यकता होती है, तो संदर्भात्मक पारदर्शिता के लिए अधिकतम करें।

एक व्यावहारिक मामले के रूप में, एक विधि-स्थानीय varसबसे सुरक्षित है varजो मौजूद है, क्योंकि यह विधि से बचती नहीं है। यदि विधि कम है, और भी बेहतर। यदि ऐसा नहीं है, तो अन्य तरीकों को निकालकर इसे कम करने का प्रयास करें।

दूसरी ओर, एक उत्परिवर्तित संग्रह में भागने की क्षमता होती है, भले ही वह ऐसा न करता हो। कोड बदलते समय, आप इसे अन्य तरीकों से पारित करना चाहते हैं, या इसे वापस कर सकते हैं। यह उस तरह की बात है जो संदर्भात्मक पारदर्शिता को तोड़ती है।

एक वस्तु (एक क्षेत्र) पर, बहुत अधिक एक ही बात होती है, लेकिन अधिक गंभीर परिणामों के साथ। किसी भी तरह से वस्तु में स्थिति होगी और इसलिए, संदर्भात्मक पारदर्शिता को तोड़ दें। लेकिन एक परिवर्तनशील संग्रह होने का मतलब यह है कि वस्तु भी खुद को खो सकती है जो इसे बदल रहा है।


39
मेरे दिमाग में अच्छी, नई बड़ी तस्वीर: immutable valओवर immutable varओवर mutable valसे अधिक पसंद करें mutable var। विशेष रूप immutable varसे खत्म mutable val!
पीटर शमित्ज़

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

1
tl; डॉ: पसंद var x: Set[Int] से अधिक val x: mutable.Set[Int] के बाद से यदि आप पारित xपूर्व मामले में, कुछ अन्य कार्य करने के लिए, आपको यह सुनिश्चित कर रहे हैं उत्परिवर्तन नहीं कर सकते हैं कि समारोह, xआप के लिए।
18

17

यदि आप अपरिवर्तनीय संग्रह के साथ काम करते हैं और आपको उन्हें "संशोधित" करने की आवश्यकता है, उदाहरण के लिए, उन्हें एक लूप में तत्व जोड़ें, तो आपको varएस का उपयोग करना होगा क्योंकि आपको परिणामी संग्रह को कहीं स्टोर करने की आवश्यकता है। यदि आप केवल अपरिवर्तनीय संग्रह से पढ़ते हैं, तो उपयोग करेंval एस का ।

सामान्य तौर पर, सुनिश्चित करें कि आप संदर्भों और वस्तुओं को भ्रमित नहीं करते हैं। vals अपरिवर्तनीय संदर्भ हैं (C में निरंतर संकेत)। है यही कारण है कि, जब आप का उपयोग करें val x = new MutableFoo(), तो आप में सक्षम हो जाएगा वस्तु को बदलने के लिए है कि xकरने के लिए अंक, लेकिन आप नहीं कर सकेंगे जो वस्तु को बदलने के लिए x अंक। यदि आप उपयोग करते हैं तो विपरीत धारण करता है var x = new ImmutableFoo()। मेरी प्रारंभिक सलाह लेना: यदि आपको संदर्भ बिंदुओं को किस ऑब्जेक्ट में बदलने की आवश्यकता नहीं है, तो valएस का उपयोग करें ।


1
var immutable = something(); immutable = immutable.update(x)एक अपरिवर्तनीय संग्रह का उपयोग करने के उद्देश्य को हराता है। आपने पहले ही रेफ़रेंशियल ट्रांसपेरेंसी छोड़ दी है और आप आमतौर पर बेहतर समय जटिलता के साथ एक म्यूटेबल संग्रह से समान प्रभाव प्राप्त कर सकते हैं। चार संभावनाओं में से ( valऔर var, परस्पर और अपरिवर्तनीय), यह सबसे कम समझ में आता है। मैं अक्सर उपयोग करता हूं val mutable
जिम पिवार्स्की

3
@JimPivarski मैं असहमत हूं, जैसा कि अन्य लोग करते हैं, डैनियल का जवाब और पीटर द्वारा टिप्पणी। यदि आपको एक डेटा संरचना को अपडेट करने की आवश्यकता है, तो एक उत्परिवर्ती घाटी के बजाय एक अपरिवर्तनीय संस्करण का उपयोग करने का फायदा है कि आप संरचना w / o जोखिम को संदर्भित कर सकते हैं कि यह दूसरों द्वारा संशोधित किया जाता है जो आपकी स्थानीय मान्यताओं को तोड़ता है। इन "दूसरों" के लिए नुकसान यह है कि वे बासी डेटा पढ़ सकते हैं।
Malte Schwerhoff

मैंने अपना मन बदल लिया है और मैं आपसे सहमत हूं (मैं इतिहास के लिए अपनी मूल टिप्पणी छोड़ रहा हूं)। मैंने तब से इसका उपयोग किया है, विशेष रूप से var list: List[X] = Nil; list = item :: list; ...और मैं भूल गया कि मैंने एक बार अलग तरह से लिखा था।
जिम पिवार्स्की

@MalteSchwerhoff: "बासी डेटा" वास्तव में वांछनीय है, यह इस बात पर निर्भर करता है कि आपने अपना कार्यक्रम कैसे बनाया है, अगर स्थिरता महत्वपूर्ण है; यह उदाहरण के लिए मुख्य अंतर्निहित सिद्धांतों में से एक है कि क्लोजर में कंसीलर कैसे काम करता है।
एरिक कप्लून

@ErikAllik मैं यह नहीं कहूंगा कि बासी डेटा प्रति सेकेण्ड वांछनीय है, लेकिन मैं इस बात से सहमत हूं कि यह पूरी तरह से ठीक हो सकता है, यह इस बात पर निर्भर करता है कि आप अपने ग्राहकों को क्या देना चाहते हैं। या क्या आपके पास एक उदाहरण है जहां बासी डेटा पढ़ने का एकमात्र तथ्य वास्तव में एक फायदा है? मैं बासी डेटा को स्वीकार करने के परिणामों का मतलब नहीं है, जो बेहतर प्रदर्शन या एक सरल एपीआई हो सकता है।
माल्टे श्वोरहॉफ़

9

इसका जवाब देने का सबसे अच्छा तरीका एक उदाहरण है। मान लीजिए कि हमारे पास कुछ प्रक्रिया है, बस किसी कारण से संख्या एकत्र कर रहे हैं। हम इन नंबरों को लॉग करना चाहते हैं, और संग्रह को दूसरे को भेजेंगे प्रक्रिया में ।

बेशक, हम लकड़हारे को संग्रह भेजने के बाद भी हम नंबर एकत्र कर रहे हैं। और मान लें कि लॉगिंग प्रक्रिया में कुछ ओवरहेड है जो वास्तविक लॉगिंग में देरी करता है। उम्मीद है कि आप देख सकते हैं कि यह कहाँ जा रहा है।

यदि हम इस संग्रह को एक परिवर्तनशील val, (परस्पर इसलिए क्योंकि हम इसे लगातार जोड़ रहे हैं) में संग्रहीत करते हैं, तो इसका मतलब है कि लॉगिंग करने वाली प्रक्रिया एक ही वस्तु को देख रही होगी जो अभी भी हमारी संग्रह प्रक्रिया द्वारा अपडेट की जा रही है। उस संग्रह को किसी भी समय अपडेट किया जा सकता है, और इसलिए जब हम लॉग करने का समय लेते हैं तो हम वास्तव में हमारे द्वारा भेजे गए संग्रह को लॉग नहीं कर सकते हैं।

यदि हम एक अपरिवर्तनीय का उपयोग करते हैं var, तो हम लकड़हारे को एक अपरिवर्तनीय डेटा संरचना भेजते हैं। हम अपने संग्रह के लिए अधिक संख्या को जोड़ते हैं, हम हो जाएगा की जगह हमारे varएक साथ नए अपरिवर्तनीय डेटा संरचना । इसका मतलब यह नहीं है कि लकड़हारे को भेजे गए संग्रह को बदल दिया गया है! यह अभी भी उस संग्रह को संदर्भित कर रहा है जो इसे भेजा गया था। तो हमारे लकड़हारा वास्तव में प्राप्त संग्रह को लॉग करेगा।


1

मुझे लगता है कि इस ब्लॉग पोस्ट में उदाहरण अधिक प्रकाश डालेंगे, क्योंकि कॉम्बो का उपयोग करने का प्रश्न समसामयिक परिदृश्यों में और भी महत्वपूर्ण हो जाता है: संगामिति के लिए अपरिवर्तनीयता का महत्व । और जब हम इस पर होते हैं, तो AtomicReference: तीन उपकरण जैसे सिंक्रनाइज़ बनाम @volatile बनाम के पसंदीदा उपयोग पर ध्यान दें


-2

var immutable बनाम val mutable

इस सवाल के कई बेहतरीन जवाबों के अलावा। यहाँ एक सरल उदाहरण है, जो संभावित खतरों का चित्रण करता हैval mutable :

म्यूटेबल ऑब्जेक्ट्स को तरीकों के अंदर संशोधित किया जा सकता है, जो उन्हें मापदंडों के रूप में लेते हैं, जबकि पुनर्मूल्यांकन की अनुमति नहीं है।

import scala.collection.mutable.ArrayBuffer

object MyObject {
    def main(args: Array[String]) {

        val a = ArrayBuffer(1,2,3,4)
        silly(a)
        println(a) // a has been modified here
    }

    def silly(a: ArrayBuffer[Int]): Unit = {
        a += 10
        println(s"length: ${a.length}")
    }
}

परिणाम:

length: 5
ArrayBuffer(1, 2, 3, 4, 10)

ऐसा कुछ नहीं हो सकता है var immutable, क्योंकि पुनर्मूल्यांकन की अनुमति नहीं है:

object MyObject {
    def main(args: Array[String]) {
        var v = Vector(1,2,3,4)
        silly(v)
        println(v)
    }

    def silly(v: Vector[Int]): Unit = {
        v = v :+ 10 // This line is not valid
        println(s"length of v: ${v.length}")
    }
}

का परिणाम:

error: reassignment to val

चूंकि फ़ंक्शन मापदंडों को माना जाता है, क्योंकि valयह पुनर्मूल्यांकन की अनुमति नहीं है।


यह गलत है। आपके द्वारा उस त्रुटि को प्राप्त करने का कारण यह है कि आपने अपने दूसरे उदाहरण में वेक्टर का उपयोग किया है, जो डिफ़ॉल्ट रूप से अपरिवर्तनीय है। यदि आप एक ArrayBuffer का उपयोग करते हैं, तो आप देखेंगे कि यह ठीक संकलित करता है और वही काम करता है जहाँ यह नए तत्व में जोड़ता है और उत्परिवर्तित बफर को प्रिंट करता है। pastebin.com/vfq7ytaD
EdgeCaseBerg

@EdgeCaseBerg, मैं जानबूझकर अपने दूसरे उदाहरण में एक वेक्टर का उपयोग कर रहा हूं, क्योंकि मैं यह दिखाने की कोशिश कर रहा हूं कि पहले उदाहरण mutable valका व्यवहार इसके साथ संभव नहीं है immutable var। यहाँ क्या गलत है?
अकवाल

आप यहां सेब की तुलना संतरे से कर रहे हैं। वेक्टर में +=सरणी बफर की तरह कोई विधि नहीं है । आपके उत्तर का अर्थ है कि +=यह वही है x = x + yजो यह नहीं है। आपका कथन जो फ़ंक्शन परमेस के रूप में व्यवहार किया जाता है, वे सही हैं और आपको वह त्रुटि मिलती है जिसका आप उल्लेख करते हैं लेकिन केवल इसलिए कि आपने उपयोग किया है =। आप एक ArrayBuffer के साथ एक ही त्रुटि प्राप्त कर सकते हैं, इसलिए यहां संग्रह परिवर्तनशीलता वास्तव में प्रासंगिक नहीं है। तो यह एक अच्छा जवाब नहीं है क्योंकि यह ओपी के बारे में बात कर रहा है पर नहीं मिल रहा है। हालांकि अगर आप इरादा नहीं रखते हैं तो यह एक परस्पर संग्रह को पारित करने के खतरों का एक अच्छा उदाहरण है।
एजकैशबर्गर

@EdgeCaseBerg लेकिन आप मेरे ArrayBufferद्वारा उपयोग किए जा रहे व्यवहार को दोहरा नहीं सकते Vector। ओपी का प्रश्न व्यापक है, लेकिन वे सुझाव दे रहे थे कि कब किसका उपयोग किया जाए, इसलिए मेरा मानना ​​है कि मेरा उत्तर उपयोगी है क्योंकि यह परिवर्तनशील संग्रह (तथ्य यह है कि valमदद नहीं करता है) के आसपास से गुजरने के खतरों को दिखाता है ; immutable varसे ज्यादा सुरक्षित है mutable val
अकवाल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.