2009-06-02 6 views
35

ठीक में एक लेटेड वैरिएबल को फिर से परिभाषित करना ठीक है। मैं क्लोजर के साथ tinkering रहा है और मैं लगातार एक ही समस्या में भागते हैं। के कोड का यह छोटा टुकड़ा लेते हैं:क्लोजर लूप

(let [x 128] 
    (while (> x 1) 
    (do 
     (println x) 
     (def x (/ x 2))))) 

अब मैं इस एक दृश्य बाहर मुद्रित करने के लिए 128 के साथ के रूप में तो शुरू करने की उम्मीद:

128 
64 
32 
16 
8 
4 
2 

इसके बजाय, यह एक अनंत लूप है, अधिक से अधिक 128 मुद्रण। स्पष्ट रूप से मेरा इरादा दुष्प्रभाव काम नहीं कर रहा है।

तो मुझे इस तरह के लूप में एक्स के मान को फिर से परिभाषित करने के लिए कैसे माना जाता है? मुझे एहसास है कि यह लिस्प की तरह नहीं हो सकता है (मैं एक अज्ञात फ़ंक्शन का उपयोग कर सकता हूं जो शायद अपने स्वयं के रिकर्स पर हो), लेकिन अगर मुझे यह पता नहीं चलता कि इस तरह चर सेट करने के लिए, मैं पागल हो जाऊंगा।

मेरा दूसरा अनुमान सेट का उपयोग करना होगा! लेकिन यह "अमान्य असाइनमेंट लक्ष्य" देता है, क्योंकि मैं बाध्यकारी रूप में नहीं हूं।

कृपया मुझे यह बताएं कि यह कैसे काम करना है।

उत्तर

48

def एक अपरिवर्तनीय var परिभाषित करता है, भले ही आप इसे किसी फ़ंक्शन या किसी कोड के आंतरिक लूप में उपयोग करते हैं। let में आपको क्या मिलता है वे वर्र्स नहीं हैं। प्रति the documentation for let:

चलो के साथ बनाए गए स्थानीय चर नहीं हैं। एक बार बनाया गया उनके मूल्य कभी नहीं बदलते हैं!

(जोर मेरा नहीं है।) आपको यहां अपने उदाहरण के लिए उत्परिवर्तनीय स्थिति की आवश्यकता नहीं है; आप loop और recur का उपयोग कर सकते हैं।

(loop [x 128] 
    (when (> x 1) 
    (println x) 
    (recur (/ x 2)))) 

आप कल्पना बनना चाहता था, तो आप स्पष्ट loop पूरी तरह से बच सकते हैं।

(let [xs (take-while #(> % 1) (iterate #(/ % 2) 128))] 
    (doseq [x xs] (println x))) 

आप वास्तव में परिवर्तनशील राज्य उपयोग करना चाहता था, तो एक atom काम हो सकता है। आप सच में, सचvars के साथ ऐसा करना चाहते हैं तो आप कुछ भयानक की तरह कर दिया था;

(let [x (atom 128)] 
    (while (> @x 1) 
    (println @x) 
    (swap! x #(/ %1 2)))) 

(while आप के लिए एक स्पष्ट एक में अपने शरीर लपेटता आप एक do जरूरत नहीं है।) इस।

(with-local-vars [x 128] 
    (while (> (var-get x) 1) 
    (println (var-get x)) 
    (var-set x (/ (var-get x) 2)))) 

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

+1

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

+4

साइड इफेक्ट्स लिस्पी हैं जिन पर आप लिस्प देख रहे हैं। सामान्य लिस्प में आप दूर हो जाएंगे (x = 128 के लिए लूप (/ x 2) जबकि (> x 1) करें (प्रिंट x) करें)। लेकिन दुष्प्रभाव क्लोजुरिश नहीं हैं। –

+1

यह बहुत पुराना है लेकिन यह एक बहुत अच्छा जवाब है, मैं क्लोजर के लिए नया हूं, इसने मुझे एक ही समस्या के साथ संघर्ष के घंटों से बचाया। बहुत बहुत धन्यवाद @ ब्रायन कैपर – shan

12

Vars पुनः नियुक्त होने के लिए नहीं कर रहे हैं (लेकिन हो सकता है) (कि तुम क्या जब आप "def" कुछ मिलता है):

user=> (def k 1) 
#'user/k 
user=> k 
1 

करने से आप को रोकने के कुछ भी नहीं है:

user=> (def k 2) 
#'user/k 
user=> k 
2 

आप एक धागे की स्थानीय settable "जगह" चाहते हैं तो आप "बाइंडिंग" और उपयोग कर सकते हैं "सेट!":

user=> (def j) ; this var is still unbound (no value) 
#'user/j 
user=> j 
java.lang.IllegalStateException: Var user/j is unbound. (NO_SOURCE_FILE:0) 
user=> (binding [j 0] j) 
0 

तो फिर आप WR कर सकते हैं यह एक लूप इस तरह है:

user=> (binding [j 0] 
     (while (< j 10) 
      (println j) 
      (set! j (inc j)))) 
0 
1 
2 
3 
4 
5 
6 
7 
8 
9 
nil 

लेकिन मुझे लगता है कि यह बहुत ही असामान्य है।

5

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

प्रासंगिक हिस्सा:

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

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

एक with-local-vars मैक्रो को लागू करने के बाद के पदों चिंताओं के बहुमत;)

1

आप अधिक मुहावरे इस्तेमाल कर सकते हैं iterate और बदले take-while,

user> (->> 128 
      (iterate #(/ % 2)) 
      (take-while (partial < 1))) 

(128 64 32 16 8 4 2) 
user>