recursion - क्लोजर में वेक्टर में परिणाम जमा करने का सबसे अच्छा तरीका?(शुद्ध कार्यात्मक कोड बदसूरत और verbose लगता है)




clojure functional-programming (4)

... शायद म्यूटेबल डेटा के साथ अनिवार्य प्रोग्रामिंग सिर्फ मेरे दिमाग में बहुत गहरी ड्रिल की गई है, लेकिन मुझे क्लोजर में डेटा के वैक्टर बनाने के लिए कोड मिल गया है, जो वर्बोज़, अनावश्यक और घुलनशील है। इसके लिए अवश्य ही एक बेहतर तरीका होना चाहिए!

रुबी में मैं कोड लिख सकता हूं जैसे:

results = []
a_collection.each do |x|
  x.nested_collection.each do |y|
    next if some_condition_holds
    results << y
  end
end

क्लोजर में, मुझे रिकर्सिव फ़ंक्शन का उपयोग करने के बजाय ऐसा करने का बेहतर तरीका पता नहीं है, शायद निम्न (भयानक) कोड की तरह:

; NEWBIE ALERT! NEWBIE ALERT!
(loop [results   []
       remaining a_collection]
  (if (empty? remaining)
      results
      (recur
        (loop [results results
               nested  (nested_collection (first remaining))]
           (if (empty? nested)
               results
               (if (some_condition_holds)
                   (recur results (rest nested))
                   (recur (conj results (first nested)) (rest nested))))) 
        (rest remaining))))

परिवर्तनीय डेटा और पुनरावृत्त लूप के बिना, आपको संग्रह बनाने के लिए रिकर्सन का उपयोग करने की आवश्यकता है। इस तरह के प्रत्येक रिकर्सिव फ़ंक्शन को एक (empty?) गार्ड क्लॉज इत्यादि की आवश्यकता होती है। पूरी बात इतनी बार दोहराई जाती है कि यह मुझे चीखना चाहती है।

साधारण मामलों में, map पर्याप्त होगा, लेकिन मैं उन मामलों के बारे में सोच रहा हूं जहां घोंसले के कई स्तर हैं, और प्रत्येक स्तर पर, ऐसी स्थितियां हो सकती हैं जिनके लिए पुनरावृत्ति को छोड़ना आवश्यक हो।

सामान्य लिस्प में मैं loop मैक्रो, या mapcan उपयोग कर सकता mapcanmapcan तरह कुछ भी नहीं है?


अगर आप एक बेवकूफ कार्यात्मक शैली का पालन करना चाहते हैं और आलसी अनुक्रम उत्पन्न करना चाहते हैं तो अमालोय का जवाब शायद सबसे अच्छा है।

यदि आप वास्तव में एक वेक्टर (आलसी अनुक्रम की बजाय) बनाने के लिए अनिवार्य रूप से रुचि रखते हैं, तो शायद मैं इसे परमाणु और डोसक का उपयोग करके करूँगा:

(let [v (atom [])]
  (doseq [x (range 5)
          y (range 5)]
    (if (> y x)
      (swap! v conj (str x y))))
  @v)

=> ["01" "02" "03" "04" "12" "13" "14" "23" "24" "34"]

जैसा कि आप देख सकते हैं, यह रूबी कोड में संरचना में बहुत समान होता है।

इसे कम करने के द्वारा ऐसा करना भी संभव है, हालांकि यह केवल तब उपयुक्त है जब केवल एक इनपुट अनुक्रम होता है, उदाहरण के लिए:

(reduce
  (fn [v x] 
    (if (even? x)
      (conj v x)
      v))
  []
  (range 20))

=> [0 2 4 6 8 10 12 14 16 18]

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


मुझे लगता है कि विकल्प कितना अच्छा लगता है के अवरोही क्रम में:

(for [x coll,
      y (nested-collection x)
      :when (not (some-condition-holds y))]
  y)

वैकल्पिक रूप से, यदि आप सिंटैक्स के उपयोग के बजाय map और mapcat जैसे कार्यों से इसे बनाना चाहते हैं:

(mapcat (fn [x]
          (remove some-condition-holds
                  (nested-collection x)))
        coll)

यदि आप वास्तव में इस पर उत्सुक हैं, तो आप इसे आंशिक फ़ंक्शन एप्लिकेशन और संरचना के साथ भी बना सकते हैं:

(mapcat (comp (partial remove some-condition-holds)
              nested-collection)
        coll)

क्लोजर में यह तीसरी शैली बहुत अच्छी तरह से पढ़ी नहीं जाती है, हालांकि कुछ अन्य भाषाओं में समकक्ष कोड बहुत अच्छा है। हास्केल में, उदाहरण के लिए:

coll >>= (filter (not . someConditionHolds) . nestedCollection)

(mapcat (fn [y] (filter condition y)) x)




lisp