2013-01-11 13 views
8

"शुद्ध रिकर्सन" यहां एक बना हुआ शब्द है, कृपया क्षमा करें।शुद्ध रिकर्सन का उपयोग कब करें और लूप/रिकर का उपयोग कब करें?

दो अलग-अलग रिकर्सन दृष्टिकोणों का उपयोग करके दो उदाहरण यहां दिए गए हैं। एक दूसरे के उपयोग के दिशानिर्देश क्या हैं?

(defn take-while 
    "Returns a lazy sequence of successive items from coll while 
    (pred item) returns true. pred must be free of side-effects." 
    {:added "1.0" 
    :static true} 
    [pred coll] 
    (lazy-seq 
    (when-let [s (seq coll)] 
     (when (pred (first s)) 
     (cons (first s) (take-while pred (rest s))))))) 

(defn take-last 
    "Returns a seq of the last n items in coll. Depending on the type 
    of coll may be no better than linear time. For vectors, see also subvec." 
    {:added "1.1" 
    :static true} 
    [n coll] 
    (loop [s (seq coll), lead (seq (drop n coll))] 
    (if lead 
     (recur (next s) (next lead)) 
     s))) 
+0

मुझे वास्तव में समझ में नहीं आता कि आप किस दिशा-निर्देश के बारे में बात कर रहे हैं। आप पहले उदाहरण में ** लूप/रिकूर ​​** रास्ता नहीं चुन सकते थे क्योंकि 'tail-while' का उपयोग ** पूंछ-स्थिति ** में नहीं किया जाता है। समस्या क्या है? ** लूप/रिकर ** हमेशा बेहतर होता है अगर इसे फंक्शन रिकर्सन कॉल से इस्तेमाल किया जा सकता है और @ मिकरा बताते हैं कि क्यों। – hsestupin

उत्तर

9

कुछ कारकों पर विचार करने के लिए:

  • पाश/पुनरावृत्ति होना ढेर अंतरिक्ष का उपभोग नहीं करता है - तो यह सही चुनाव है कि अगर आप गहराई से नेस्टेड प्रत्यावर्तन है कि अन्यथा एक कारण हो सकता है क्या करने जा रहे StackOverflowError
  • पाश/पुनरावृत्ति होना तेजी से होता है - यह Clojure में सबसे कुशल निर्माणों में से एक सही ढंग से किया यह जावा कोड में पाश के लिए एक बराबर की गति से मेल खाना चाहिए है,
  • सामान्य प्रत्यावर्तन अधिक मुहावरेदार है - औसतन यह आप साफ, अधिक कार्यात्मक कोड देने के लिए है, जबकि पाश/पुनरावृत्ति होना एक अनिवार्य, पुनरावृत्ति शैली
  • पाश/पुनरावृत्ति होना अधिक प्रतिबंध है की दिशा में आप और अधिक बढ़ाने के लिए जाता है जाता है - आप केवल पूंछ की स्थिति में पुनरावृत्ति कर सकते हैं, आप दो अलग-अलग कार्यों, आदि के बीच पारस्परिक पुनरावृत्ति नहीं कर सकते हैं। कभी-कभी लूप/पुनरावृत्ति कार्य करना संभव नहीं होता है, दूसरी बार आपको ऐसा करने के लिए अपने कोड को विपरीत करने की आवश्यकता हो सकती है।
+1

क्या संकलक लूप/रिकूर ​​फॉर्म (या लूप/रिकूर ​​फॉर्म के लिए उत्पन्न कोड) में उपरोक्त समय (जहां रिकर्सन आखिरी है) में फॉर्म के बेवकूफ रिकर्सन को बदलने के लिए संभव नहीं होगा? – Hendekagon

+0

@ हेंडेकैगन: इस मामले में नहीं, क्योंकि रिकर्सन पूंछ की स्थिति में नहीं है। सैद्धांतिक रूप से यह अन्य परिस्थितियों में हो सकता है, लेकिन मेरा मानना ​​है कि यह रिच हिकी द्वारा स्वचालित रूप से ऐसा नहीं करने का एक डिज़ाइन निर्णय था - इसलिए यदि आप पूंछ की पुनरावृत्ति चाहते हैं, तो आपको इसे 'रिकूर' के साथ स्पष्ट रूप से पूछना होगा – mikera

2

केवल एक lazy-seq/lazy-cons तंत्र का उपयोग करने के कारण आलसी दृश्यों पैदा कर रहा है। यदि आपको उनकी आवश्यकता नहीं है तो loop/recur निस्संदेह उपयोग किया जाना चाहिए।

1

जब आप अपना कार्य पहले स्थान पर लिख रहे हों तो सादा रिकर्सन का उपयोग करें। फिर जब आप इसे कर सकें तो एक बार फिर से काम करने के बाद इसे दोबारा बदल दें।

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