2010-07-16 24 views
26

मैं clojure कोड में हाल ही में निम्नलिखित मुहावरा का उपयोग कर अपने आप को मिल गया है।क्लोजर में ग्लोबल्स के लिए सर्वश्रेष्ठ अभ्यास, (रेफ बनाम बदलते-var-root)?

(def *some-global-var* (ref {})) 

(defn get-global-var [] 
    @*global-var*) 

(defn update-global-var [val] 
    (dosync (ref-set *global-var* val))) 

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

उत्तर

21

आपके कार्यों के दुष्प्रभाव हैं। उन्हें एक ही इनपुट के साथ दो बार कॉल करना *some-global-var* के वर्तमान मूल्य के आधार पर अलग-अलग रिटर्न मान दे सकता है। इससे चीजों को परीक्षण करना और कारण बनना मुश्किल हो जाता है, खासतौर पर एक बार जब आप इन वैश्विक वार्सों में से एक से अधिक हो जाते हैं।

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

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

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

(def *address-book* (ref {})) 

(defn add [name addr] 
    (dosync (alter *address-book* assoc name addr))) 

(defn report [] 
    (doseq [[name addr] @*address-book*] 
    (println name ":" addr))) 

(defn do-some-stuff [] 
    (add "Brian" "123 Bovine University Blvd.") 
    (add "Roger" "456 Main St.") 
    (report)) 

अलगाव में do-some-stuff को देखते हुए, यह बिल्ली क्या कर रहा है:

यहाँ एक अत्यधिक काल्पनिक उदाहरण है? बहुत सारी चीजें पूरी तरह से हो रही हैं। इस मार्ग से नीचे स्पेगेटी है। एक यकीनन बेहतर संस्करण:

(defn make-address-book [] {}) 

(defn add [addr-book name addr] 
    (assoc addr-book name addr)) 

(defn report [addr-book] 
    (doseq [[name addr] addr-book] 
    (println name ":" addr))) 

(defn do-some-stuff [] 
    (let [addr-book (make-address-book)] 
    (-> addr-book 
     (add "Brian" "123 Bovine University Blvd.") 
     (add "Roger" "456 Main St.") 
     (report)))) 

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

आप एक से अधिक थ्रेड से डेटा संरचना के लिए अद्यतन का समन्वय करने की जरूरत नहीं है, वहां आमतौर पर refs या वैश्विक वार्स उपयोग करने की आवश्यकता है।

+6

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

+0

सभी कार्यों के मूल्य को निश्चित रूप से एक अच्छा विकल्प पास कर रहा है, लेकिन मुझे लगता है कि कभी-कभी वैश्विक वैरिएबल अधिक से अधिक कार्यों के समूह के मूल्य के गुच्छा को पार करने से कहीं अधिक व्यावहारिक होता है। यह दुष्प्रभावों के लिए स्वाद और सहिष्णुता का विषय है। – ChrisBlom

26

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

उदा। कुंजी/मूल्य जोड़े के एक परिवर्तनीय मानचित्र के लिए मैं उपयोग करूंगा:

(def state (atom {})) 

(defn get-state [key] 
    (@state key)) 

(defn update-state [key val] 
    (swap! state assoc key val)) 
+0

यह मेरे पसंदीदा दृष्टिकोण के करीब है, मैं साझा स्थान घोषित करने के लिए एक ही खरीद उपयोग defonce करता हूं, इसे ओवरराइट करने से बचने के लिए, और इसे स्पष्ट करने के लिए नाम में क्षुद्रग्रहों का उपयोग करें यह एक साझा स्थान है – ChrisBlom

+0

प्रतीक में तारों का उपयोग करना नाम अब एक कंपाइलर चेतावनी में परिणाम देता है क्योंकि ये गतिशील वर्रों के लिए आरक्षित हैं। – spieden