आमतौर पर भाषाओं में निर्मित अपरिवर्तनीय प्रकार जो अपरिवर्तनशीलता के इर्द-गिर्द नहीं घूमते हैं, वांछित परिवर्तन को व्यक्त करने के लिए कुछ "बिल्डर" प्रकार की वस्तु की आवश्यकता होने पर संभावित उपयोग करने के लिए अधिक डेवलपर समय खर्च करने की आवश्यकता होगी (इसका मतलब यह नहीं है कि समग्र काम अधिक होगा, लेकिन इन मामलों में लागत में बढ़ोतरी है)। इस बात की परवाह किए बिना कि क्या यह वास्तव में अपरिवर्तनीय प्रकार बनाने में आसान है या नहीं, यह हमेशा गैर-तुच्छ डेटा प्रकारों के लिए कुछ प्रसंस्करण और मेमोरी ओवरहेड की आवश्यकता होगी।
बनाना साइड इफेक्ट्स से रहित
यदि आप उन भाषाओं में काम कर रहे हैं जो अपरिवर्तनीयता के आसपास नहीं घूमती हैं, तो मुझे लगता है कि व्यावहारिक दृष्टिकोण हर एक डेटा प्रकार को अपरिवर्तनीय बनाने की तलाश नहीं है। संभावित रूप से अधिक उत्पादक मानसिकता जो आपको एक ही लाभ प्रदान करती है, वह है आपके सिस्टम में उन कार्यों की संख्या को अधिकतम करने पर ध्यान केंद्रित करना जो शून्य साइड इफेक्ट का कारण बनते हैं ।
एक सरल उदाहरण के रूप में, यदि आपके पास एक फ़ंक्शन है जो इस तरह के दुष्प्रभाव का कारण बनता है:
// Make 'x' the absolute value of itself.
void make_abs(int& x);
तब हमें एक अपरिवर्तनीय पूर्णांक डेटा प्रकार की आवश्यकता नहीं होती है जो उस फ़ंक्शन को साइड इफेक्ट से बचने के लिए पोस्ट-इनिशियलाइज़ेशन असाइनमेंट जैसे ऑपरेटरों को मना करती है। हम बस यह कर सकते हैं:
// Returns the absolute value of 'x'.
int abs(int x);
अब फ़ंक्शन x
इसके दायरे से बाहर या उसके साथ कुछ भी गड़बड़ नहीं करता है , और इस तुच्छ मामले में हमने अप्रत्यक्ष / अलियासिंग से जुड़े किसी भी ओवरहेड से बचने के लिए कुछ चक्रों को भी मुंडा दिया हो सकता है। बहुत कम से कम दूसरा संस्करण पहले की तुलना में अधिक कम्प्यूटेशनल रूप से महंगा नहीं होना चाहिए।
वे चीजें जो कॉपी करने के लिए महंगी हैं
निश्चित रूप से ज्यादातर मामलों में यह तुच्छ नहीं है अगर हम एक कार्य कारण दुष्प्रभाव से बचना चाहते हैं। एक जटिल वास्तविक दुनिया का उपयोग मामला इस तरह हो सकता है:
// Transforms the vertices of the specified mesh by
// the specified transformation matrix.
void transform(Mesh& mesh, Matrix4f matrix);
जिस बिंदु पर मेष को एक सौ सौ से अधिक बहुभुजों के साथ एक सौ मेगाबाइट मेमोरी की आवश्यकता हो सकती है, यहां तक कि अधिक कोने और किनारों, कई बनावट के नक्शे, मॉर्फ लक्ष्य आदि, यह वास्तव में महंगा होगा कि इसे बनाने के लिए पूरे जाल की नकल करें। transform
समारोह के साइड इफेक्ट्स से मुक्त, जैसे:
// Returns a new version of the mesh whose vertices been
// transformed by the specified transformation matrix.
Mesh transform(Mesh mesh, Matrix4f matrix);
और यह इन मामलों में है जहां इसकी संपूर्णता में कुछ कॉपी करना आम तौर पर एक महाकाव्य ओवरहेड होगा जहां मैंने इसे Mesh
लगातार डेटा संरचना में बदलने के लिए उपयोगी पाया है और एनालॉग "बिल्डर" के साथ एक अपरिवर्तनीय प्रकार इसके संशोधित संस्करण बनाने के लिए इतना है कि यह बस उथले प्रतिलिपि और संदर्भ गणना भागों को देख सकते हैं जो अद्वितीय नहीं हैं। यह मेष कार्यों को लिखने में सक्षम होने के ध्यान के साथ है, जो दुष्प्रभावों से मुक्त हैं।
लगातार डेटा संरचनाएं
और इन मामलों में जहां सब कुछ कॉपी करना इतना अविश्वसनीय रूप से महंगा है, मुझे Mesh
वास्तव में भुगतान करने के लिए एक अपरिवर्तनीय डिजाइन करने का प्रयास मिला, भले ही इसकी थोड़ी सी खड़ी लागत थी, क्योंकि यह थ्रेड सुरक्षा को सरल नहीं करता था। यह गैर-विनाशकारी संपादन को भी सरल करता है (उपयोगकर्ता को अपनी मूल प्रति को संशोधित किए बिना मेष संचालन को परत करने की अनुमति देता है), पूर्ववत सिस्टम (अब पूर्ववत व्यवस्था केवल मेमोरी को उड़ाने के बिना एक ऑपरेशन द्वारा किए गए परिवर्तनों से पहले जाल की एक अपरिवर्तनीय प्रतिलिपि संग्रहीत कर सकती है) उपयोग), और अपवाद-सुरक्षा (अब यदि उपरोक्त फ़ंक्शन में कोई अपवाद होता है, तो फ़ंक्शन को वापस रोल नहीं करना पड़ता है और इसके सभी साइड इफेक्ट्स को पूर्ववत करना पड़ता है क्योंकि इससे कोई शुरुआत नहीं हुई थी)।
मैं इन मामलों में विश्वासपूर्वक कह सकता हूं कि इन भारी डेटा संरचनाओं को अपरिवर्तनीय बनाने में लगने वाले समय की लागत की तुलना में अधिक समय की बचत हुई है, क्योंकि मैंने इन नए डिजाइनों के रखरखाव की लागत की तुलना उन पूर्व के खिलाफ की है जो परिवर्तनशीलता और कार्यों के चारों ओर घूमते हैं और दुष्प्रभाव पैदा करते हैं, और पूर्व के परिवर्तनशील डिजाइनों में अधिक समय लगता है और मानव त्रुटि के लिए कहीं अधिक संभावित थे, विशेष रूप से ऐसे क्षेत्रों में जो क्रंच समय के दौरान डेवलपर्स के लिए लुभावना होते हैं, जैसे अपवाद-सुरक्षा।
इसलिए मुझे लगता है कि इन मामलों में अपरिवर्तनीय डेटा प्रकार वास्तव में भुगतान करते हैं, लेकिन आपके सिस्टम में अधिकांश कार्यों को साइड इफेक्ट से मुक्त बनाने के लिए सब कुछ अपरिवर्तनीय नहीं होना चाहिए। बहुत सी चीजें काफी सस्ती हैं जो सिर्फ पूर्ण में कॉपी हैं। इसके अलावा कई वास्तविक दुनिया के अनुप्रयोगों को यहां और वहां कुछ साइड इफेक्ट्स की आवश्यकता होगी (बहुत कम से कम एक फ़ाइल को बचाने की तरह), लेकिन आमतौर पर ऐसे कई कार्य हैं जो साइड इफेक्ट्स से रहित हो सकते हैं।
मेरे पास कुछ अपरिवर्तनीय डेटा प्रकार होने की बात यह सुनिश्चित करने के लिए है कि हम केवल छोटे हिस्से होने पर बाएं और दाएं पूर्ण रूप से गहरी डेटा संरचनाओं की नकल के बिना महाकाव्य ओवरहेड के साइड इफेक्ट्स से मुक्त होने के लिए अधिकतम कार्य लिख सकते हैं। उनमें से बदलाव की जरूरत है। उन मामलों में लगातार डेटा स्ट्रक्चर्स होने के बाद हमें ऐसा करने के लिए एक महाकाव्य लागत का भुगतान किए बिना साइड इफेक्ट से मुक्त होने के लिए हमें अपने कार्यों को लिखने की अनुमति देने के लिए एक अनुकूलन विवरण बन जाता है।
अपरिवर्तनीय ओवरहेड
अब वैचारिक रूप से उत्परिवर्तित संस्करणों में हमेशा दक्षता में बढ़त होगी। वहाँ हमेशा कम्प्यूटेशनल ओवरहेड अपरिवर्तनीय डेटा संरचनाओं के साथ जुड़ा हुआ है। लेकिन मैंने इसे ऊपर वर्णित मामलों में एक योग्य विनिमय पाया, और आप ओवरहेड को प्रकृति में पर्याप्त रूप से न्यूनतम बनाने पर ध्यान केंद्रित कर सकते हैं। मैं उस प्रकार के दृष्टिकोण को पसंद करता हूं जहां शुद्धता आसान हो जाती है और अनुकूलन आसान होने के बजाय अनुकूलन कठिन हो जाता है लेकिन शुद्धता कठिन हो जाती है। यह कोड के बारे में अवमूल्यन करने के रूप में लगभग नहीं है कि कोड पर कुछ और धुन अप की आवश्यकता के लिए पूरी तरह से सही ढंग से कार्य करता है जो पहली बार में सही ढंग से काम नहीं करता है चाहे वह कितनी जल्दी अपने गलत परिणामों को प्राप्त करता हो।