2010-06-01 21 views
38

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

(defn sieve [potentials primes] 
    (if-let [p (first potentials)] 
    (recur (filter #(not= (mod % p) 0) potentials) (conj primes p)) 
    primes)) 

छोटे पर्वतमाला यह ठीक काम करता है के लिए, लेकिन बड़े श्रेणियों के लिए एक ढेर अतिप्रवाह कारण बनता है:

user=> (sieve (range 2 30) []) 
[2 3 5 7 11 13 17 19 23 29] 
user=> (sieve (range 2 15000) []) 
java.lang.StackOverflowError (NO_SOURCE_FILE:0) 

मैंने सोचा था कि recur का उपयोग करके यह एक गैर होगा -स्टैक उपभोग लूपिंग निर्माण? मैं क्या खो रहा हूँ?

+12

+1 आपके प्रश्न के शीर्षक में स्टैक ओवरफ़्लो रखने के लिए – radman

+0

मजेदार; मेरे लिये कार्य करता है। क्लोजर का कौन सा संस्करण आप किस प्लेटफॉर्म पर जेवीएम के साथ उपयोग कर रहे हैं? क्या आप ओवरफ्लो के बिना '(रेंज 2 15000) 'चला सकते हैं? –

+0

उबंटू 9 .10, जावा 1.6.0_15, क्लोजर का नवीनतम स्नैपशॉट 1.2.0 – dbyrne

उत्तर

53

आपको filter की आलस्य से मारा जा रहा है। अपने recur फॉर्म में (filter ...) से (doall (filter ...)) बदलें और समस्या दूर होनी चाहिए।

एक और अधिक में गहराई से स्पष्टीकरण:

filter करने के लिए कॉल एक आलसी seq देता है, जो seq फ़िल्टर्ड की वास्तविक तत्व materializes के रूप में की आवश्यकता है। लिखित के रूप में, आपका कोड filterfilter पर filter पर filter पर ढेर करता है ..., प्रत्येक पुनरावृत्ति पर filter आईएनजी का एक और स्तर जोड़ना; कुछ बिंदु पर यह उड़ाता है। समाधान प्रत्येक पुनरावृत्ति पर पूरे परिणाम को मजबूर करना है ताकि अगला व्यक्ति पूरी तरह से एहसास सीक पर फ़िल्टरिंग कर सके और आलसी सीक प्रसंस्करण की एक अतिरिक्त परत जोड़ने के बजाय पूरी तरह से एहसास हुआ सीक वापस कर दे; यह doall करता है।

+0

धन्यवाद! यह मेरी समस्या तय है। उत्कृष्ट स्पष्टीकरण। – dbyrne

+0

कोई विचार यह कैसे पता लगाएं? शायद मैक्रोएक्सपैंड की तरह कुछ? – edbond

+4

स्टैक ट्रेस पर एक नज़र डालें, मैं कहूंगा। 'Clojure.lang.LazySeq 'विधि कॉल का ढेर एक अच्छा संकेत होगा कि समस्या आलस्य से संबंधित है। –

0

एल्गोरिदमिक रूप से समस्या यह है कि जब आप इसका कोई और उद्देश्य नहीं लेते हैं तो आप फ़िल्टरिंग जारी रखते हैं। रोक को जल्द से जल्द प्रत्यावर्तन गहराई में द्विघात कमी (sqrt(n) बनाम n) को प्राप्त होता है: 16,000 (1862 के बजाय सिर्फ 30 पुनरावृत्तियों प्रदर्शन) के लिए

(defn sieve [potentials primes]  
    (if-let [p (first potentials)] 
     (if (> (* p p) (last potentials)) 
     (concat primes potentials) 
     (recur (filter (fn [n] (not= (mod n p) 0)) potentials) 
       (conj primes p))) 
    primes)) 

चलाता है ठीक है, और भी के लिए 160,000, on ideonedoall के बिना भी 5% तेज चलता है।

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