क्या संगामिति नहीं होने पर अपरिवर्तनीयता बहुत सार्थक है?


53

ऐसा लगता है कि अपरिवर्तनीय प्रकारों और विशेष रूप से संग्रह का उपयोग करने के मुख्य लाभ के रूप में थ्रेड-सेफ्टी को हमेशा / अक्सर उल्लेख किया जाता है।

मेरे पास एक ऐसी स्थिति है जहां मैं यह सुनिश्चित करना चाहूंगा कि एक विधि तार के एक शब्दकोश को संशोधित नहीं करेगी (जो सी # में अपरिवर्तनीय हैं)। मैं जितना संभव हो सके चीजों को कसना चाहता हूं।

हालाँकि मुझे यकीन नहीं है कि एक नए पैकेज (Microsoft अपरिवर्तनीय संग्रह) के लिए निर्भरता जोड़ना इसके लायक है। प्रदर्शन भी कोई बड़ा मुद्दा नहीं है।

इसलिए मुझे लगता है कि मेरा सवाल यह है कि क्या अपरिवर्तनीय संग्रह की जोरदार सलाह दी जाती है जब कोई कठिन प्रदर्शन आवश्यकताएं नहीं होती हैं और थ्रेड सुरक्षा मुद्दे नहीं होते हैं? उस मूल्य शब्दार्थ पर विचार करें (जैसा कि मेरे उदाहरण में) आवश्यकता के रूप में भी हो सकता है या नहीं भी हो सकता है।


1
समवर्ती संशोधन का अर्थ थ्रेड्स की आवश्यकता नहीं है। केवल उपयुक्त नाम पर देखें, ConcurrentModificationExceptionजो आमतौर पर एक ही धागे में एक ही धागे में संग्रह को उत्परिवर्तित करने के कारण होता है, एक ही संग्रह में foreachलूप के शरीर में ।

1
मेरा मतलब है कि आप गलत नहीं हैं, लेकिन यह ओपी से जो पूछ रहा है, उससे अलग है। अपवाद को फेंक दिया जाता है क्योंकि गणना के दौरान संशोधन की अनुमति नहीं है। एक समवर्ती छायांकन का उपयोग, उदाहरण के लिए, अभी भी यह त्रुटि होगी।
एडथर्थर्ड

13
हो सकता है कि आपको खुद से विपरीत सवाल भी पूछना चाहिए: जब पारस्परिकता सार्थक है?
जियोर्जियो

2
जावा में, यदि परिवर्तनशीलता प्रभावित होती है hashCode()या equals(Object)परिणाम बदला जा रहा है, तो इसका उपयोग करते समय त्रुटियां हो सकती हैं Collections(उदाहरण के लिए, HashSetऑब्जेक्ट को "बाल्टी" में संग्रहीत किया गया था और उत्परिवर्तन के बाद इसे दूसरे में जाना चाहिए)।
SJuan76

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

जवाबों:


101

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

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


23
+1 कॉनसेरी एक साथ उत्परिवर्तन है, लेकिन समय के साथ फैलने वाला उत्परिवर्तन कारण के रूप में कठिन हो सकता है
guillaume31

20
इस पर विस्तार करने के लिए: एक फ़ंक्शन जो एक परिवर्तनशील चर पर निर्भर है, को एक अतिरिक्त छिपे हुए तर्क के रूप में सोचा जा सकता है, जो कि परिवर्तनशील चर का वर्तमान मूल्य है। कोई भी फ़ंक्शन जो एक परिवर्तनशील चर को बदलता है, उसी तरह एक अतिरिक्त रिटर्न मान का उत्पादन करने के बारे में सोचा जा सकता है, यानी कि उत्परिवर्तनीय राज्य का नया मूल्य। जब कोड का एक टुकड़ा देख रहे हों तो आपके पास कोई विचार नहीं है यदि यह निर्भर करता है या परिवर्तनशील स्थिति को बदलता है, तो आपको इसका पता लगाना होगा और फिर मानसिक रूप से परिवर्तनों का ट्रैक रखना होगा। यह कोड के किसी भी दो टुकड़ों के बीच युग्मन का भी परिचय देता है जो परस्पर स्थिति साझा करते हैं, और युग्मन खराब है।
डोभाल

29
@ मेहरदाद लोग दशकों तक विधानसभा में बड़े कार्यक्रमों को क्रैक करने में कामयाब रहे। फिर हमने कुछ दशकों के सी
डोभाल

11
जब वस्तुएं बड़ी होती हैं तो @Mehrdad संपूर्ण ऑब्जेक्ट को कॉपी करना एक अच्छा विकल्प नहीं है। मैं यह नहीं देखता कि सुधार के मामलों में परिमाण का क्रम क्यों शामिल है। क्या आप उत्पादकता में 20% सुधार (नोट: मनमानी संख्या) को केवल इसलिए बंद कर देंगे क्योंकि यह ट्रिपल-डिजिट में सुधार नहीं था? अपरिवर्तनीयता एक समझदार डिफ़ॉल्ट है ; आप इससे भटक सकते हैं , लेकिन आपको एक कारण चाहिए।
डोभाल

9
@ जियोर्जियो स्काला ने मुझे एहसास दिलाया कि कैसे एक बार आपको एक मूल्य को भी बदलने योग्य बनाने की आवश्यकता होती है। जब भी मैं उस भाषा का उपयोग करता हूं, मैं सब कुछ करता हूं val, और केवल बहुत, बहुत कम अवसरों पर मुझे पता चलता है कि मुझे कुछ को एक में बदलने की आवश्यकता है var। किसी भी दिए गए भाषा में बहुत सारे 'वैरिएबल' परिभाषित करते हैं, बस एक मूल्य रखने के लिए हैं जो कुछ गणना के परिणाम को संग्रहीत करता है, और इसे अपडेट करने की आवश्यकता नहीं है।
KChaloux

22

आपका कोड आपके इरादे को व्यक्त करना चाहिए। यदि आप किसी ऑब्जेक्ट को एक बार बनाए जाने के बाद संशोधित नहीं करना चाहते हैं, तो उसे संशोधित करना असंभव है।

अपरिवर्तनशीलता के कई लाभ हैं:

  • मूल लेखक का इरादा बेहतर व्यक्त किया गया है।

    आप यह कैसे जान पाएंगे कि निम्नलिखित कोड में, नाम को संशोधित करने से एप्लिकेशन कहीं बाद में अपवाद उत्पन्न करेगा?

    public class Product
    {
        public string Name { get; set; }
    
        ...
    }
    
  • यह सुनिश्चित करना आसान है कि ऑब्जेक्ट अमान्य स्थिति में प्रकट नहीं होगा।

    आपको इसे एक कंस्ट्रक्टर में, और केवल वहीं नियंत्रित करना होगा। दूसरी ओर, यदि आपके पास बसने वाले और तरीके हैं जो ऑब्जेक्ट को संशोधित करते हैं, तो इस तरह के नियंत्रण विशेष रूप से मुश्किल हो सकते हैं, खासकर जब उदाहरण के लिए, दो क्षेत्रों को एक ही समय में बदलना चाहिए ताकि ऑब्जेक्ट वैध हो।

    उदाहरण के लिए, एक ऑब्जेक्ट वैध है अगर पता नहीं है null या जीपीएस निर्देशांक नहीं हैं null, लेकिन यह अमान्य है यदि पता और जीपीएस निर्देशांक दोनों निर्दिष्ट किए गए हैं। क्या आप यह कल्पना करने के लिए नरक की कल्पना कर सकते हैं कि क्या पता और जीपीएस निर्देशांक में एक सेटर है, या दोनों परस्पर हैं?

  • संगामिति।

वैसे, आपके मामले में, आपको किसी तीसरे पक्ष के पैकेज की आवश्यकता नहीं है। .NET फ्रेमवर्क में पहले से ही एक ReadOnlyDictionary<TKey, TValue>वर्ग शामिल है ।


1
+1, विशेष रूप से "आपको एक कंस्ट्रक्टर में इसे नियंत्रित करना होगा, और केवल वहां।" IMO यह एक बहुत बड़ा लाभ है।
जियोर्जियो

10
एक और लाभ: एक वस्तु की नकल करना मुफ्त है। बस एक पॉइंटर।
रॉबर्ट ग्रांट

1
@MainMa आपके उत्तर के लिए धन्यवाद, लेकिन जहां तक ​​मैं समझता हूं कि ReadOnlyDictionary कोई गारंटी नहीं देता है कि कोई और अंतर्निहित शब्दकोश नहीं बदलेगा (यहां तक ​​कि संगोष्ठी के बिना मैं मूल शब्द के संदर्भ को ऑब्जेक्ट के भीतर सहेजना चाहता हूं जो विधि से संबंधित है बाद में उपयोग के लिए)। ReadOnlyDictionary भी एक अजीब नामस्थान में घोषित किया गया है: System.Collections.ObjectModel।
डेन

2
@ डेन: यह मेरे एक पालतू जानवर से संबंधित है: "रीड-ओनली" और "अपरिवर्तनीय" लोगों को पर्यायवाची के रूप में। यदि किसी ऑब्जेक्ट को केवल-पढ़ने के लिए रैपर में एनकैप्सुलेट किया गया है और कोई अन्य संदर्भ मौजूद नहीं है या ब्रह्मांड में कहीं भी रखा गया है, तो ऑब्जेक्ट को लपेटने से यह अपरिवर्तनीय हो जाएगा, और रैपर के संदर्भ को शॉर्टहैंड के रूप में उपयोग किया जा सकता है उसमें निहित वस्तु। हालाँकि, कोई भी तंत्र नहीं है जिसके द्वारा यह पता लगाया जा सके कि क्या मामला है। इसके विपरीत, क्योंकि आवरण एक लपेटी गई वस्तु के प्रकार को छिपाता है, एक अपरिवर्तनीय वस्तु को
लपेटता है

2
... कोड के लिए यह जानना असंभव बना देगा कि परिणामी आवरण सुरक्षित रूप से अपरिवर्तनीय माना जा सकता है या नहीं।
सुपर जट

13

अपरिवर्तनीयता का उपयोग करने के कई एकल-थ्रेडेड कारण हैं। उदाहरण के लिए

ऑब्जेक्ट A में ऑब्जेक्ट B शामिल है।

बाहरी कोड आपके ऑब्जेक्ट B पर सवाल उठाता है और आप इसे वापस कर देते हैं।

अब आपके पास तीन संभावित परिस्थितियाँ हैं:

  1. B अपरिवर्तनीय है, कोई समस्या नहीं है।
  2. B परिवर्तनशील है, आप एक रक्षात्मक प्रतिलिपि बनाते हैं और उसे वापस करते हैं। प्रदर्शन हिट लेकिन कोई जोखिम नहीं।
  3. B परस्पर है, आप इसे वापस करते हैं।

तीसरे मामले में उपयोगकर्ता कोड को एहसास नहीं हो सकता है कि आपने क्या किया है और ऑब्जेक्ट में बदलाव कर सकते हैं, और ऐसा करने से आपके ऑब्जेक्ट का आंतरिक डेटा बदल जाता है जिसके साथ आपका कोई नियंत्रण या दृश्यता नहीं हो रही है।


9

अपरिवर्तनशीलता भी कचरा संग्रहकर्ताओं के कार्यान्वयन को बहुत सरल कर सकती है। से GHC का विकी :

[...] डेटा अपरिवर्तनीयता हमें बहुत सारे अस्थायी डेटा का उत्पादन करने के लिए मजबूर करती है, लेकिन यह इस कचरे को तेजी से इकट्ठा करने में भी मदद करती है। चाल यह है कि अपरिवर्तनीय डेटा कभी भी युवा मूल्यों की ओर इशारा नहीं करता है। वास्तव में, पुराने मूल्य अभी तक उस समय मौजूद नहीं हैं जब एक पुराना मूल्य बनाया जाता है, इसलिए इसे खरोंच से नहीं बताया जा सकता है। और चूंकि मूल्यों को कभी संशोधित नहीं किया जाता है, न ही इसे बाद में इंगित किया जा सकता है। यह अपरिवर्तनीय डेटा की प्रमुख संपत्ति है।

यह कचरा संग्रहण (GC) को बहुत सरल करता है। किसी भी समय हम बनाए गए अंतिम मानों को स्कैन कर सकते हैं और उन्हें मुक्त कर सकते हैं जो एक ही सेट से इंगित नहीं किए जाते हैं (बेशक, लाइव मूल्यों की वास्तविक जड़ें पदानुक्रम ढेर में रहते हैं)। [...] तो इसका एक काउंटर-सहज व्यवहार है: आपके मूल्यों का बड़ा प्रतिशत कचरा है - यह तेजी से काम करता है। [...]


5

क्या पर विस्तार KChaloux बहुत अच्छी तरह से संक्षेप ...

आदर्श रूप से, आपके पास दो प्रकार के फ़ील्ड हैं, और इसलिए उनका उपयोग करके दो प्रकार के कोड हैं। या तो फ़ील्ड अपरिवर्तनीय हैं, और कोड को उत्परिवर्तन को ध्यान में नहीं रखना है; या फ़ील्ड परिवर्तनशील हैं, और हमें कोड लिखने की ज़रूरत है जो या तो स्नैपशॉट लेता है ( int x = p.x) या इनायत से ऐसे परिवर्तनों को संभालता है।

मेरे अनुभव में, अधिकांश कोड आशावादी कोड होने के नाते दोनों के बीच में आते हैं : यह स्वतंत्र रूप से उत्परिवर्तित डेटा का संदर्भ देता है, यह मानते हुए कि पहली कॉल p.xका दूसरा कॉल के समान परिणाम होगा। और ज्यादातर समय, यह सच है, सिवाय इसके कि जब यह निकलता है तो अब नहीं है। उफ़।

तो, वास्तव में, उस प्रश्न को चारों ओर घुमाएं: इस परिवर्तन को करने के लिए मेरे कारण क्या हैं ?

  • स्मृति आवंटन को कम करना / मुक्त करना?
  • स्वभाव से म्यूट? (उदाहरण के लिए काउंटर)
  • संशोधक बचाता है, क्षैतिज शोर? (स्थिरांक / अंतिम)
  • कुछ कोड को छोटा / आसान बनाता है? (init डिफ़ॉल्ट, संभवतः बाद में ओवरराइट करें)

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


3

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

यह भाषाई में एक प्रसिद्ध घटना है कि कुछ शब्द बहुत अधिक दिखाई देते हैं, अन्य संदर्भ में भी प्रकट हो सकते हैं। इसलिए कई Stringऑब्जेक्ट बनाने के बजाय , आप एक अपरिवर्तनीय का उपयोग कर सकते हैं। लेकिन फिर आपको इन अपरिवर्तनीय वस्तुओं की देखभाल के लिए एक पूल प्रबंधक रखने की आवश्यकता है।

यह आपको बहुत सारी मेमोरी को बचाएगा। यह भी पढ़ने के लिए एक दिलचस्प लेख है: http://en.wikipedia.org/wiki/Zipf%27s_law


1

जावा, C # और अन्य समान भाषाओं में, क्लास-टाइप फ़ील्ड्स का उपयोग ऑब्जेक्ट्स की पहचान करने के लिए, या उन ऑब्जेक्ट्स में मान या स्थिति को एन्कैप्सुलेट करने के लिए किया जा सकता है, लेकिन भाषाओं में इस तरह के उपयोग के बीच कोई अंतर नहीं होता है। मान लीजिए कि एक क्लास ऑब्जेक्ट Georgeमें एक प्रकार का क्षेत्र है char[] chars;। वह क्षेत्र या तो किसी वर्ण अनुक्रम को अलग कर सकता है:

  1. एक सरणी जिसे कभी संशोधित नहीं किया जाएगा, और न ही इसे संशोधित करने वाले किसी भी कोड के संपर्क में, लेकिन बाहर के संदर्भ मौजूद हो सकते हैं।

  2. एक सरणी जिसके लिए कोई बाहरी संदर्भ मौजूद नहीं है, लेकिन जॉर्ज स्वतंत्र रूप से संशोधित कर सकता है।

  3. एक सरणी जो कि जॉर्ज के स्वामित्व में है, लेकिन इससे बाहर के दृश्य मौजूद हो सकते हैं जो जॉर्ज की वर्तमान स्थिति का प्रतिनिधित्व करने की उम्मीद करेंगे।

इसके अतिरिक्त, चर एक चरित्र अनुक्रम encapsulating के बजाय, किसी अन्य वस्तु के स्वामित्व वाले चरित्र अनुक्रम के लिए एक लाइव दृश्य encapsulate कर सकते हैं

यदि charsवर्तमान में वर्ण अनुक्रम [पवन] charsको एनकैप्सुलेट किया जाता है , और जॉर्ज चरित्र अनुक्रम [छड़ी] को एनकैप्सुलेट करना चाहता है, तो कई चीजें हैं जो जॉर्ज कर सकता है:

A. अक्षर युक्त एक नई सरणी का निर्माण करें [भटकें] और charsपुराने के बजाय उस सरणी को पहचानने के लिए बदलें ।

B. किसी तरह पहले से मौजूद चरित्र सरणी को पहचानें जो हमेशा पात्रों [वांड] को रखेगा और charsपुराने के बजाय उस सरणी को पहचानने के लिए बदल देगा ।

सी सरणी से पहचान के दूसरे चरित्र बदले charsएक करने के लिए a

वांछित परिणाम प्राप्त करने के लिए 1, (ए) और (बी) सुरक्षित तरीके हैं। मामले में (2), (ए) और (सी) सुरक्षित हैं, लेकिन (बी) ऐसा नहीं होगा [यह तत्काल समस्याओं का कारण नहीं होगा, लेकिन चूंकि जॉर्ज यह मान लेगा कि उसके पास सरणी का स्वामित्व है, इसलिए यह मान लेगा कि यह वसीयत में सरणी बदल सकता है]। मामले में (3), विकल्प (ए) और (बी) किसी भी बाहरी विचारों को तोड़ देंगे, और इस प्रकार केवल विकल्प (सी) सही है। इस प्रकार, क्षेत्र द्वारा समझाया गया वर्ण अनुक्रम को संशोधित करने का तरीका जानने के लिए यह जानना आवश्यक है कि यह किस प्रकार का क्षेत्र है।

यदि एक प्रकार के क्षेत्र का उपयोग करने के बजाय char[], जो एक संभावित-उत्परिवर्तनीय वर्ण अनुक्रम को कूटबद्ध करता है, तो कोड ने प्रकार का उपयोग किया है String, जो एक अपरिवर्तनीय वर्ण अनुक्रम को कूटबद्ध करता है, उपरोक्त सभी मुद्दे दूर हो जाते हैं। प्रकार के सभी फ़ील्ड Stringएक साझा करने योग्य ऑब्जेक्ट का उपयोग करके वर्णों के अनुक्रम को एनकैप्सुलेट करते हैं जो कभी नहीं बदलेगा। नतीजतन, यदि एक प्रकार का क्षेत्रString"विंड" को एनकैप्सुलेट करता है, इसे बनाने का एकमात्र तरीका "वैंड" है जो इसे अलग-अलग ऑब्जेक्ट की पहचान करने के लिए बनाता है - जो "वैंड" रखता है। ऐसे मामलों में जहां कोड ऑब्जेक्ट का एकमात्र संदर्भ रखता है, ऑब्जेक्ट को उत्परिवर्तित करना एक नया बनाने की तुलना में अधिक कुशल हो सकता है, लेकिन किसी भी समय एक वर्ग के परस्पर भिन्न होने के लिए अलग-अलग तरीकों के बीच अंतर करना आवश्यक है जिसके द्वारा यह मूल्य को संक्षिप्त कर सकता है। व्यक्तिगत रूप से मुझे लगता है कि इसके लिए Apps हंगेरियन का उपयोग किया जाना चाहिए था (मैं चार char[]प्रकारों का शब्दार्थ-विशिष्ट प्रकारों पर विचार करूँगा , भले ही टाइप सिस्टम उन्हें समान मानता है - ठीक उसी तरह की स्थिति जहाँ Apps हंगेरियन चमकता है), लेकिन चूंकि यह इस तरह की अस्पष्टताओं से बचने का सबसे आसान तरीका अपरिवर्तनीय प्रकारों को डिज़ाइन करना है जो केवल मूल्यों को एक तरह से घेरते हैं।


यह एक उचित उत्तर की तरह दिखता है, लेकिन पढ़ना और समझना थोड़ा कठिन है।
डेन

1

यहां कुछ अच्छे उदाहरण हैं, लेकिन मैं कुछ व्यक्तिगत लोगों के साथ कूदना चाहता था जहां अपरिवर्तनीयता ने एक टन की मदद की। मेरे मामले में, मैंने मुख्य रूप से ओवरलैपिंग रीड और राइट के साथ समानांतर रूप से कोड चलाने में सक्षम होने और दौड़ की स्थिति के बारे में चिंता न करने की उम्मीद के साथ एक अपरिवर्तनीय समवर्ती डेटा संरचना डिजाइन करना शुरू कर दिया। एक बात थी जो जॉन कार्मैक ने मुझे उस तरह की प्रेरणा दी, जिससे वह इस तरह के विचार के बारे में बात कर सके। यह इस तरह लागू करने के लिए एक बहुत ही बुनियादी संरचना और बहुत तुच्छ है:

यहाँ छवि विवरण दर्ज करें

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

हालांकि, मैं यह नहीं मिला है किबहुआयामी उद्देश्यों के लिए उपयोगी। आखिरकार, अभी भी वैचारिक समस्या है जहां, कहते हैं, एक भौतिकी प्रणाली समवर्ती रूप से भौतिकी को लागू करती है जबकि एक खिलाड़ी दुनिया में तत्वों को स्थानांतरित करने की कोशिश कर रहा है। ट्रांसफ़ॉर्म किए गए डेटा की कौन सी अपरिवर्तनीय प्रतिलिपि आपके साथ जाती है, एक खिलाड़ी जो रूपांतरित होता है या एक फिजिक्स सिस्टम ट्रांसफ़ॉर्म किया जाता है? इसलिए मुझे वास्तव में इस बुनियादी वैचारिक समस्या का एक अच्छा और सरल समाधान नहीं मिला है सिवाय परस्पर डेटा संरचनाओं के जो कि एक स्मार्ट तरीके से लॉक होते हैं और ओवरलैपिंग को हतोत्साहित करते हैं और स्टडिंग थ्रेड्स से बचने के लिए बफर के एक ही सेक्शन को लिखते हैं। ऐसा कुछ जॉन कार्मैक को लगता है कि संभवतः उसे अपने खेल में हल करने के लिए पता चला है; कम से कम वह इसके बारे में बात करता है जैसे वह कीड़े के एक कार को खोलने के बिना लगभग एक समाधान देख सकता है। मैं उस संबंध में उससे दूर नहीं गया हूं। मैं देख सकता हूँ सभी अंतहीन सवाल हैं अगर मैं बस के आसपास सब कुछ समानांतर की कोशिश की। काश, मैं एक दिन उनके मस्तिष्क पर उठाता, क्योंकि मेरे अधिकांश प्रयास उन विचारों के साथ शुरू होते थे, जिन्हें वह फेंक देते थे।

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

सिस्टम को पूर्ववत करें

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

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

on user operation:
    copy entire application state to undo entry
    perform operation

on undo/redo:
    swap application state with undo entry

आम तौर पर जब आपके दृश्य डेटा को पूरी तरह से कॉपी करने के लिए गीगाबाइट का विस्तार होता है, तो ऊपर का कोड काफी अक्षम होगा। लेकिन यह डेटा संरचना केवल उथली प्रतियों को कॉपी करती है, जिन्हें बदला नहीं गया था, और इसने इसे वास्तव में काफी सस्ते में पूरे एप्लिकेशन राज्य की एक अपरिवर्तनीय प्रतिलिपि बना दिया था। इसलिए अब मैं पूर्ववत कोड के रूप में आसानी से पूर्ववत प्रणालियों को लागू कर सकता हूं और इस अपरिवर्तनीय डेटा संरचना का उपयोग करने पर ध्यान केंद्रित कर सकता हूं ताकि एप्लिकेशन स्टेट के अपरिवर्तित भागों को सस्ता और सस्ता और सस्ता बनाया जा सके। जब से मैंने इस डेटा संरचना का उपयोग करना शुरू किया है, मेरी सभी व्यक्तिगत परियोजनाओं में इस सरल पैटर्न का उपयोग करते हुए पूर्ववत सिस्टम हैं।

अब भी यहाँ कुछ ओवरहेड है। पिछली बार जब मैंने मापा था कि यह लगभग 10 किलोबाइट था, तो इसे लागू करने के लिए बिना किसी बदलाव के पूरे आवेदन राज्य की नकल करना (यह दृश्य की जटिलता से स्वतंत्र है क्योंकि दृश्य एक पदानुक्रम में व्यवस्थित है, इसलिए यदि जड़ से नीचे कुछ भी नहीं बदलता है, केवल जड़ है बच्चों में नीचे उतरने के बिना उथले की नकल की जाती है)। यह 0 बाइट्स से बहुत दूर है क्योंकि एक पूर्ववत व्यवस्था के लिए केवल डेल्टास के भंडारण की आवश्यकता होगी। लेकिन प्रति ऑपरेशन 10 किलोवॉट के ओवरहेड पर, यह अभी भी प्रति 100 उपयोगकर्ता संचालन में केवल मेगाबाइट है। इसके अलावा मैं अभी भी संभावित रूप से स्क्वाश कर सकता हूं ताकि भविष्य में जरूरत पड़ने पर और नीचे पहुंच सके।

अपवाद-सुरक्षा

एक जटिल अनुप्रयोग के साथ अपवाद-सुरक्षा कोई तुच्छ मामला नहीं है। हालाँकि, जब आपकी एप्लिकेशन स्थिति अपरिवर्तनीय है और आप केवल परमाणु परिवर्तन लेनदेन करने की कोशिश करने के लिए क्षणिक वस्तुओं का उपयोग कर रहे हैं, तो यह स्वाभाविक रूप से अपवाद-सुरक्षित है क्योंकि यदि कोड का कोई भी हिस्सा फेंकता है, तो एक नई अपरिवर्तनीय प्रतिलिपि देने से पहले क्षणिक को हटा दिया जाता है । ताकि मैं हमेशा एक जटिल सी ++ कोडबेस में सही पाने के लिए सबसे कठिन चीजों में से एक को तुच्छ समझता हूं।

बहुत से लोग अक्सर C ++ में RAII के अनुरूप संसाधनों का उपयोग करते हैं और सोचते हैं कि यह अपवाद-सुरक्षित होने के लिए पर्याप्त है। अक्सर ऐसा नहीं होता है, क्योंकि एक फ़ंक्शन आम तौर पर अपने दायरे के स्थानीय लोगों से परे राज्यों को दुष्प्रभाव पैदा कर सकता है। आपको आम तौर पर उन मामलों में स्कोप गार्ड और परिष्कृत रोलबैक लॉजिक के साथ काम करना शुरू करना होगा। इस डेटा संरचना ने इसे बनाया है इसलिए मुझे अक्सर इसके साथ परेशान होने की आवश्यकता नहीं है क्योंकि फ़ंक्शन साइड इफेक्ट का कारण नहीं हैं। वे आवेदन की स्थिति को बदलने के बजाय आवेदन राज्य की अपरिवर्तनीय प्रतियाँ लौटा रहे हैं।

गैर-विनाशकारी संपादन

यहाँ छवि विवरण दर्ज करें

गैर-विनाशकारी संपादन मूल उपयोगकर्ता के डेटा (बस इनपुट डेटा और आउटपुट डेटा को छूने के बिना इनपुट) को छूने के बिना एक साथ लेयरिंग / स्टैकिंग / कनेक्टिंग ऑपरेशन है। यह आमतौर पर फ़ोटोशॉप की तरह एक साधारण छवि अनुप्रयोग के साथ लागू करने के लिए तुच्छ है और इस डेटा संरचना से इतना लाभ नहीं हो सकता है क्योंकि कई ऑपरेशन बस पूरी छवि के प्रत्येक पिक्सेल को बदलना चाहते हैं।

हालांकि, गैर-विनाशकारी मेष संपादन के साथ, उदाहरण के लिए, बहुत सारे ऑपरेशन अक्सर मेष के केवल एक हिस्से को बदलना चाहते हैं। एक ऑपरेशन बस कुछ कोने को यहां स्थानांतरित करना चाह सकता है। एक और सिर्फ वहाँ कुछ बहुभुज को वश में करना चाहते हो सकता है। यहां अपरिवर्तनीय डेटा संरचना एक टन को पूरे जाल की एक पूरी प्रतिलिपि बनाने की आवश्यकता से बचने में मदद करती है बस जाल के एक नए हिस्से को बदलने के लिए इसका नया संस्करण वापस करने के लिए।

कम से कम साइड इफेक्ट

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

उदाहरण के लिए, आमतौर पर पदों का एक गुच्छा बदलने का प्रलोभन एक मैट्रिक्स और वस्तुओं की एक सूची को स्वीकार करने और एक परिवर्तनशील फैशन में बदलने के लिए हो सकता है। इन दिनों मैं खुद को सिर्फ वस्तुओं की एक नई सूची में वापस पाता हूं।

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

सस्ते कॉपियों के लाभ

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

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


0

पहले से ही बहुत अच्छे जवाब हैं। यह कुछ अतिरिक्त जानकारी है जो .NET के लिए विशिष्ट है। मैं पुराने .NET ब्लॉग पोस्टों के माध्यम से खुदाई कर रहा था और Microsoft अपरिवर्तनीय सिद्धांतों के दृष्टिकोण से लाभ का एक अच्छा योग पाया गया:

  1. स्नैपशॉट शब्दार्थ, आपको अपने संग्रह को इस तरह से साझा करने की अनुमति देता है कि रिसीवर कभी नहीं बदल सकता है।

  2. बहु-थ्रेडेड एप्लिकेशन में इंप्लांटेड थ्रेड-सेफ्टी (संग्रह को एक्सेस करने के लिए आवश्यक कोई लॉक नहीं)।

  3. किसी भी समय आपके पास एक वर्ग का सदस्य होता है जो एक संग्रह प्रकार को स्वीकार या वापस करता है और आप अनुबंध में केवल पढ़ने के लिए शब्दार्थ को शामिल करना चाहते हैं।

  4. कार्यात्मक प्रोग्रामिंग के अनुकूल।

  5. गणना के दौरान संग्रह के संशोधन की अनुमति दें, जबकि मूल संग्रह सुनिश्चित नहीं करता है।

  6. वे उसी IReadOnly को लागू करते हैं * इंटरफेस कि आपका कोड पहले से ही इतने माइग्रेशन से निपटता है आसान है।

यदि कोई आपको ReadOnlyCollection, IReadOnlyList, या IEnumerable एकमात्र गारंटी देता है, तो आप डेटा को बदल नहीं सकते हैं - इस बात की कोई गारंटी नहीं है कि जिस व्यक्ति ने आपको संग्रह सौंपा है, वह इसे नहीं बदलेगा। फिर भी, आपको अक्सर कुछ विश्वास की आवश्यकता होती है कि यह नहीं बदलेगा। जब आपकी सामग्री बदल जाती है तो ये प्रकार आपको सूचित करने के लिए घटनाओं की पेशकश नहीं करते हैं, और यदि वे बदलते हैं, तो हो सकता है कि एक अलग थ्रेड पर हो, संभवतः जब आप इसकी सामग्री की गणना कर रहे हों? इस तरह के व्यवहार से आपके आवेदन में डेटा भ्रष्टाचार और / या यादृच्छिक अपवाद पैदा होंगे।

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