2014-04-30 7 views
10

कुछ कारणों से रूबी बाएं रिकर्सन का सामना करते समय बेहतर प्रदर्शन कर रहा है। उदाहरण के लिए:रूबी बाएं बनाम सही रिकर्सन

def left_recursive_factorial(number) 
    return 1 if number.zero? 
    left_recursive_factorial(number.pred) * number 
end 

def right_recursive_factorial(number) 
    return 1 if number.zero? 
    number * right_recursive_factorial(number.pred) 
end 

जब मैं 9000 से अधिक एक मूल्य के साथ इन तरीकों() मैं अलग परिणाम प्राप्त फोन:

left_recursive_factorial(9001) 
    # => factorial of 9001 

right_recursive_factorial(9001) 
    # => SystemStackError: stack level too deep 
    # from (pry):6:in `right_recursive_factorial' 

मैं इस व्यवहार के लिए कोई स्पष्टीकरण नहीं पा सके।

कुछ हद तक संबंधित चीज लगभग LL() पार्सर्स को बाएं रिकर्सन के साथ समस्याएं थीं और मुझे लगता है कि आप इसे चारों ओर फ़्लिप कर सकते हैं, लेकिन मैंने इसमें बहुत कुछ खोला नहीं है।

क्या कोई थोड़ा और विस्तार से बता सकता है कि अलग-अलग प्रदर्शन करने के लिए बाएं और दाएं रिकर्सन का कारण बनता है (आमतौर पर और रूबी विशेष रूप से) और यदि आपके पास एक या दूसरे को चुनने की संभावना है तो आप इसे क्यों चुनेंगे (और क्यों छोड़ा गया था रुबी में चुना गया)?

+0

ऐसा लगता है कि रूबी बाएं से पहले गुणा के दाहिने तरफ का मूल्यांकन करता है, और इसलिए बायां संस्करण [पूंछ रिकर्सन] (https://en.wikipedia.org/wiki/Tail_call) का उपयोग करता है, इसलिए इसे नहीं करना है ढेर पर जोड़ें। – clcto

+0

@clcto: यह पूंछ कॉल उन्मूलन की तरह दिखता नहीं है। एक के लिए, गुणा समारोह में अंतिम ऑपरेशन है, रिकर्सिव कॉल नहीं। दूसरे के लिए, यदि आप केवल 9500 तक की संख्या बढ़ाते हैं तो भी आप स्टैक को उड़ा देंगे। – Chuck

+4

@clcto रूबी सबसे निश्चित रूप से ऑपरेटरों और विधि तर्कों को बाएं से दाएं, दाएं से बाएं नहीं मानते हैं। इसके अलावा जिस क्रम में ऑपरेंड का मूल्यांकन किया जाता है, वह इस बात के संबंध में अप्रासंगिक है कि कुछ पूंछ रिकर्सिव है या नहीं। गुणा आवश्यक रूप से फ़ंक्शन कॉल के बाद होता है (क्योंकि जब तक आप दोनों संख्याओं को नहीं जानते हैं तब तक आप दो संख्याओं को गुणा नहीं कर सकते हैं), इसलिए विधि पूंछ-रिकर्सिव नहीं है, इससे कोई फर्क नहीं पड़ता कि किस नंबर का मूल्यांकन पहले किया जाता है। और किसी भी तरह से मानक रूबी दुभाषिया पूंछ रिकर्सन अनुकूलित नहीं करता है। – sepp2k

उत्तर

6

ठीक है, मैं किसी भी प्रकार का एक YARV हैकर नहीं हूं, लेकिन यहां अंतर है क्योंकि मैं इसे समझता हूं। जब आप कोई विधि कॉल करते हैं, प्रेषक विधि के तर्कों को स्टैक पर धक्का देता है, तो कॉल की गई विधि इसके तर्क बंद कर देती है और इसके वापसी मूल्य को धक्का देती है। पहले रिकर्सिव कॉल के साथ, number तर्क अभी तक ढेर पर धक्का नहीं दिया गया है, इसलिए प्रत्येक कॉल का ढेर थोड़ा कम स्थान लेता है। यही कारण है कि आप उस संस्करण से कुछ और पुनरावृत्तियों को प्राप्त कर सकते हैं, लेकिन बहुत अधिक नहीं - आप ढेर के उपयोग में कुछ प्रतिशत कमी देख रहे हैं।

+0

यह समझ में आता है, लेकिन मैं थोड़ा अधिक विस्तृत उत्तर की उम्मीद कर रहा था। उदाहरण के लिए पाइथन और PHP क्यों उदासीन लगते हैं, जबकि जेएस समान व्यवहार दिखाता है। – ndn

+1

@ndn: यह भाषा कार्यान्वयन पर अत्यधिक निर्भर है। उदाहरण के लिए, पायथन में एक रिकर्सन सीमा है, जो एक वास्तविक मूल्य है कि दुभाषिया रिकर्सिव कॉल की जांच करता है और इनकार कर देता है। आप इसे 'sys.setrecursionlimit() 'के साथ बदल सकते हैं। PHP के मामले में, मुझे एक डिस्सेबलर पकड़ना पड़ा क्योंकि मुझे नहीं पता था कि यह कैसे काम करता है। यह तर्कों को धक्का देने और फिर गुणा करने के लिए स्टैक-मशीन दृष्टिकोण नहीं लेता है। इसके बजाए, यह वास्तव में 'एमयू' ओपोड के संचालन के रूप में ऑपरेटरों को '*' देता है, इसलिए कार्यों के बीच एकमात्र अंतर 'एमयूएल! 0, $ 2' बनाम' एमयूएल $ 2,! 0' है। – Chuck

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