2014-12-12 8 views
8

मान लीजिए कि आप एक पुनरावर्ती समारोह एक लेट ब्लॉक में परिभाषित करते हैं:क्या रिकर्सिव चलो एफएन को याद करने का कोई आसान तरीका है?

(let [fib (fn fib [n] 
      (if (< n 2) 
       n 
       (+ (fib (- n 1)) 
       (fib (- n 2)))))] 
    (fib 42)) 

यह यंत्रवत् memoize उपयोग करने के लिए परिवर्तित किया जा सकता:

  1. लपेटें memoize के लिए एक कॉल में fn प्रपत्र।
  2. फ़ंक्शन नाम को पहले तर्क के रूप में ले जाएं।
  3. जहां भी इसे कहा जाता है, फ़ंक्शन को अपने आप पास करें।
  4. partial का उपयोग करके फ़ंक्शन प्रतीक को रीबंड करें।

उपरोक्त कोड को बदलने के लिए सुराग के लिए:

(let [fib (memoize 
      (fn [fib n] 
       (if (< n 2) 
       n 
       (+ (fib fib (- n 1)) 
        (fib fib (- n 2)))))) 
     fib (partial fib fib)] 
    (fib 42)) 

यह काम करता है, लेकिन बहुत ज्यादा जटिल लगता है। सवाल यह है: क्या कोई आसान तरीका है?

+0

आपको इस मामले में टीसीओ का उपयोग करना चाहिए ('लूप' का उपयोग करके क्लोजर में सामान्य परिभाषा में टीसीओ नहीं है)। – Hauleth

+0

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

उत्तर

3

मैं जवाब देने में जोखिम लेता हूं क्योंकि मैं विद्वान नहीं हूं लेकिन मुझे ऐसा नहीं लगता है। आपने मानक चीज को काफी हद तक सही किया है जो ठीक से एक निश्चित बिंदु संयोजक के माध्यम से ज्ञापन का आंशिक अनुप्रयोग है।

आप मैक्रोज़ के साथ परेशान करने का प्रयास कर सकते हैं (सरल मामलों के लिए यह आसान हो सकता है, वाक्यविन्यास-उद्धरण आपके लिए नाम समाधान करेगा और आप उस पर काम कर सकते हैं)। घर आने के बाद मैं कोशिश करूंगा।

संपादित करें: वापस घर चला गया और सामान बाहर करने की कोशिश की, इस साधारण मामलों

(defmacro memoize-rec [form] 
    (let [[fn* fname params & body] form 
     params-with-fname (vec (cons fname params))] 
    `(let [f# (memoize (fn ~params-with-fname 
         (let [~fname (partial ~fname ~fname)] [email protected])))] 
     (partial f# f#)))) 

;; (clojure.pprint/pprint (macroexpand '(memoize-rec (fn f [x] (str (f x)))))) 
((memoize-rec (fn fib [n] 
       (if (< n 2) 
        n 
        (+ (fib (- n 1)) 
        (fib (- n 2)))))) 75) ;; instant 

((fn fib [n] 
       (if (< n 2) 
        n 
        (+ (fib (- n 1)) 
        (fib (- n 2))))) 75) ;; slooooooow 

मैं क्या सोचा से अधिक आसान के लिए ठीक-ish हो रहा है!

+0

निश्चित बिंदु संयोजक पहलू दिलचस्प है। शायद 'ज्ञापन' कॉल के परिणामस्वरूप अप्रत्यक्ष रूप से पुनरावर्ती करने के लिए कोई भी दृष्टिकोण या तो इस तरह के एक संयोजक का उपयोग करेगा या वैकल्पिक रूप से कुछ वैश्विक राज्य पेश करेगा (उदाहरण के लिए, यह समस्या उत्पन्न नहीं होती है जब 'लेट' ब्लॉक में नहीं, और 'def' एक var ing, जो आवश्यक संकेत प्रदान करता है।) तो शायद कोई _fundamentally_ सरल समाधान नहीं है। लेकिन, वाक्य रचनात्मक रूप से, आपका मैक्रो अद्भुत है। यह अच्छी तरह से यांत्रिक परिवर्तन का ख्याल रखता है (बिना कोड-चलने वाले मैक्रो के!), और परिणामस्वरूप होता है जो 'ज्ञापन' कॉल के रूप में सरल होता है। –

1

मुझे यकीन नहीं है कि यह प्रति "सरल" है, लेकिन मैंने सोचा कि मैं एक सीपीएस ट्रांसफार्मर के लिए letfn को दोबारा लागू करने के लिए एक दृष्टिकोण साझा करूंगा।

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

(let [f nil] (set! f (memoize (fn [] <body-of-f>))) (f))

बेशक यह इसी रूप में काम नहीं करता है, क्योंकि let बाइंडिंग Clojure में अडिग हैं।

(let [f (promise)] (deliver! f (memoize (fn [] <body-of-f>))) (@f))

लेकिन यह अभी भी कम पड़ता है, क्योंकि हम (deref f) साथ <body-of-f> में f के प्रत्येक उदाहरण बदलना होगा: उदाहरण के लिए, एक promise - हम, कुछ मिल सकता है, हालांकि, एक संदर्भ प्रकार का उपयोग करके। लेकिन हम इसे एक और समारोह शुरू करके हल कर सकते हैं जो वादे में संग्रहीत समारोह को आमंत्रित करता है।तो पूरे समाधान इस प्रकार दिखाई देंगे:

(let [f* (promise)] (letfn [(f [] (@f*))] (deliver f* (memoize (fn [] <body-of-f>))) (f)))

आप परस्पर पुनरावर्ती कार्यों का एक सेट है, तो:

(let [f* (promise) g* (promise)] (letfn [(f [] (@f*)) (g [] (@g*))] (deliver f* (memoize (fn [] (g)))) (deliver g* (memoize (fn [] (f)))) (f)))

जाहिर है कि बॉयलर-प्लेट की एक बहुत कुछ है। लेकिन यह एक मैक्रो बनाने के लिए बहुत छोटा है जो आपको letfn -स्टाइल वाक्यविन्यास देता है।

संबंधित मुद्दे

 संबंधित मुद्दे