पुनरावृत्ति या Iteration?


226

क्या एक प्रदर्शन हिट है अगर हम एल्गोरिदम में पुनरावृत्ति या इसके विपरीत लूप का उपयोग करते हैं जहां दोनों एक ही उद्देश्य की सेवा कर सकते हैं? उदाहरण: दिए गए स्ट्रिंग एक पैलिंड्रोम है या नहीं इसकी जाँच करें। मैंने कई प्रोग्रामर को एक साधारण पुनरावृत्ति एल्गोरिथ्म बिल को फिट करने के लिए दिखावा करने के साधन के रूप में पुनरावृत्ति का उपयोग करते देखा है। क्या संकलक यह तय करने में महत्वपूर्ण भूमिका निभाता है कि क्या उपयोग करना है?


4
@ सरदार हमेशा नहीं। शतरंज कार्यक्रमों के साथ, उदाहरण के लिए, पुनरावृत्ति को पढ़ना आसान है। शतरंज कोड का "पुनरावृत्त" संस्करण वास्तव में गति में मदद नहीं करेगा, और इसे और अधिक जटिल बना सकता है।
मतीन उल्हाक

12
किसी आरी के ऊपर हथौड़े से वार क्यों किया जाना चाहिए? एक अक्ल पर एक पेचकश? एक बरमा पर एक छेनी?
वेन कॉनराड

3
कोई पसंदीदा नहीं हैं। वे सभी सिर्फ उपकरण हैं, प्रत्येक का अपना उद्देश्य है। मैं पूछता हूँ, "किस प्रकार की समस्याएं पुनरावृत्ति की तुलना में पुनरावृत्ति बेहतर हैं, और इसके विपरीत?"
वेन कॉनराड

9
"रिकर्सियन के बारे में क्या अच्छा है?" ... यह पुनरावर्ती है कि क्या है। ; ओ)
केंग

9
मिथ्या आधार। पुनरावृत्ति अच्छा नहीं है; वास्तव में यह बहुत बुरा है। कोई भी मजबूत सॉफ्टवेयर लिखने के बाद से सभी पुनरावृत्ति को खत्म करने की कोशिश करेगा, जब तक कि इसे टेल-कॉल ऑप्टिमाइज़ नहीं किया जा सकता है या लॉगरिदमिक या समान रूप से बंधे स्तरों की संख्या, पुनरावृत्ति लगभग हमेशा खराब प्रकार के अतिप्रवाह को रोकती है।
आर .. गिटहब स्टॉप हेल्पिंग आईसीई

जवाबों:


181

यह संभव है कि पुनरावर्ती कार्य पूंछ पुनरावर्ती (अंतिम पंक्ति पुनरावर्ती कॉल) है , तो इस पर निर्भर करते हुए, अधिक महंगा होगा । पूंछ की पुनरावृत्ति को कंपाइलर द्वारा पहचाना जाना चाहिए और इसके पुनरावृत्ति समकक्ष के लिए अनुकूलित किया जाना चाहिए (संक्षिप्त, स्पष्ट कार्यान्वयन को आपके कोड में रखते समय)।

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


12
एल्गोरिदम जिनकी शुद्धता को प्रेरण द्वारा साबित किया जा सकता है वे खुद को पुनरावर्ती रूप में स्वाभाविक रूप से लिखते हैं। इस तथ्य के साथ युग्मित किया जाता है कि पूंछ की पुनरावृत्ति संकलक द्वारा अनुकूलित की जाती है, आप अंत में पुनरावर्ती व्यक्त किए गए अधिक एल्गोरिदम को देखकर समाप्त होते हैं।
बिनील थॉमस

15
पुन: tail recursion is optimized by compilersलेकिन सभी संकलक पूंछ पुनरावृत्ति का समर्थन नहीं करते हैं ..
केविन मेरेडिथ

347

लूप्स आपके प्रोग्राम के लिए एक प्रदर्शन लाभ प्राप्त कर सकते हैं। पुनरावृत्ति आपके प्रोग्रामर के लिए एक प्रदर्शन लाभ प्राप्त कर सकती है। चुनें जो आपकी स्थिति में अधिक महत्वपूर्ण है!


3
@LeighCaldwell: मुझे लगता है कि मेरी सोच बिल्कुल सही है। अफ़सोस की बात नहीं है। मेरे पास निश्चित रूप से है। :)
एंडी टर्नर

35
क्या आप जानते हैं कि आपके उत्तर वाक्यांश के कारण आपको एक पुस्तक में उद्धृत किया गया था? LOL amazon.com/Grokking-A
एल्गोरिथ्म-illustrated

4
मुझे यह उत्तर पसंद है .. और मुझे "ग्रॉकिंग एल्गोरिथम" पुस्तक पसंद है
मैक्स

इसलिए, कम से कम मेरे और 341 मनुष्यों ने ग्रॉकिंग एल्गोरिदम किताब पढ़ी!
zzfima

78

पुनरावृत्ति की तुलना पुनरावृत्ति से करने के लिए एक फिलिप्स सिर पेचकश की तुलना एक फ्लैट सिर पेचकश से करने की तरह है। अधिकांश भाग के लिए आप एक फ्लैट सिर के साथ किसी भी फिलिप्स के सिर के पेंच को हटा सकते हैं , लेकिन यह सिर्फ आसान होगा यदि आपने उस पेंच के लिए डिज़ाइन किए गए पेचकश का उपयोग किया है?

कुछ एल्गोरिदम सिर्फ खुद को पुनरावृत्ति के लिए उधार देते हैं क्योंकि वे जिस तरह से डिज़ाइन किए गए हैं (फाइबोनैचि अनुक्रम, संरचना की तरह एक पेड़ को पीछे छोड़ते हैं, आदि)। पुनरावृत्ति एल्गोरिथम को अधिक संक्षिप्त और समझने में आसान बनाता है (इसलिए साझा करने योग्य और पुन: प्रयोज्य)।

इसके अलावा, कुछ पुनरावर्ती एल्गोरिदम "आलसी मूल्यांकन" का उपयोग करते हैं जो उन्हें अपने चलने वाले भाइयों की तुलना में अधिक कुशल बनाता है। इसका मतलब यह है कि वे केवल उस समय की महंगी गणना करते हैं जब उन्हें लूप के चलने के बजाय हर बार की जरूरत होती है।

आपको शुरू करने के लिए पर्याप्त होना चाहिए। मैं आपके लिए कुछ लेख और उदाहरण भी खोदूंगा।

लिंक 1: Haskel बनाम PHP (पुनरावृत्ति बनाम Iteration)

यहां एक उदाहरण है जहां प्रोग्रामर को PHP का उपयोग करके एक बड़े डेटा सेट को संसाधित करना था। वह दिखाता है कि रिकसेशन का उपयोग करके हास्केल से निपटना कितना आसान रहा होगा, लेकिन चूंकि PHP के पास समान विधि को पूरा करने का कोई आसान तरीका नहीं था, इसलिए उसे परिणाम प्राप्त करने के लिए पुनरावृत्ति का उपयोग करने के लिए मजबूर किया गया।

http://blog.webspecies.co.uk/2011-05-31/lazy-evaluation-with-php.html

लिंक 2: पुनरावृत्ति को प्राप्त करना

अधिकांश पुनरावृत्ति की खराब प्रतिष्ठा उच्च लागत और अक्षम भाषाओं में अक्षमता से आती है। इस लेख के लेखक के बारे में बात करता है कि कैसे पुनरावर्ती एल्गोरिदम को अनुकूलित करने के लिए उन्हें तेज और अधिक कुशल बनाया जाए। वह यह भी बताता है कि पारंपरिक पाश को पुनरावर्ती कार्य में कैसे बदला जाए और पूंछ के अंत की पुनरावृत्ति का उपयोग करने के लाभ। उनके समापन शब्दों ने मेरे कुछ महत्वपूर्ण बिंदुओं को अभिव्यक्त किया है जो मुझे लगता है:

"पुनरावर्ती प्रोग्रामिंग प्रोग्रामर को एक तरह से कोड को व्यवस्थित करने का एक बेहतर तरीका देता है जो कि अनुरक्षणीय और तार्किक रूप से सुसंगत है।"

https://developer.ibm.com/articles/l-recurs/

लिंक 3: क्या लूपिंग की तुलना में पुनरावृत्ति कभी तेज होती है? (उत्तर)

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

क्या लूपिंग की तुलना में पुनरावृत्ति कभी तेज होती है?


4
वास्तव में पेचकश सादृश्य पसंद आया
j3314


16

स्मृति में पुनरावृत्ति अधिक महंगा है, क्योंकि प्रत्येक पुनरावर्ती कॉल में आमतौर पर मेमोरी पते को स्टैक पर धकेलने की आवश्यकता होती है - ताकि बाद में कार्यक्रम उस बिंदु पर वापस आ सके।

फिर भी, ऐसे कई मामले हैं जिनमें पुनरावृत्ति छोरों की तुलना में बहुत अधिक प्राकृतिक और पठनीय है - जैसे कि पेड़ों के साथ काम करते समय। इन मामलों में मैं पुनरावृत्ति से चिपके रहने की सलाह दूंगा।


5
जब तक कि आपका कंपाइलर स्काला की तरह टेल कॉल का अनुकूलन नहीं करता है।
बेन हार्डी

11

आमतौर पर, एक प्रदर्शन के दंड के दूसरी दिशा में झूठ होने की उम्मीद करता है। पुनरावर्ती कॉल अतिरिक्त स्टैक फ्रेम के निर्माण के लिए नेतृत्व कर सकते हैं; इसके लिए जुर्माना अलग-अलग है। इसके अलावा, पायथन जैसी कुछ भाषाओं में (अधिक सही ढंग से, कुछ भाषाओं के कुछ कार्यान्वयनों में ...), आप उन कार्यों के लिए आसानी से स्टैक सीमा में भाग सकते हैं, जिन्हें आप पुनरावर्ती रूप से निर्दिष्ट कर सकते हैं, जैसे कि ट्री डेटा संरचना में अधिकतम मान प्राप्त करना। इन मामलों में, आप वास्तव में छोरों के साथ रहना चाहते हैं।

अच्छा पुनरावर्ती कार्य लिखना प्रदर्शन के दंड को कुछ हद तक कम कर सकता है, यह मानते हुए कि आपके पास एक संकलक है जो पूंछ की पुनरावृत्ति का अनुकूलन करता है, आदि (यह सुनिश्चित करने के लिए दोहराएं कि फ़ंक्शन वास्तव में पूंछ पुनरावर्ती है --- यह उन चीजों में से एक है जो कई लोग गलतियां करते हैं। पर।)

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


8

पुनरावृत्ति उन समस्याओं के लिए पुनरावृत्ति से बेहतर है जिन्हें कई , छोटे टुकड़ों में तोड़ा जा सकता है ।

उदाहरण के लिए, एक पुनरावर्ती Fibonnaci एल्गोरिथ्म बनाने के लिए, आप फ़ाइबर (n) को फ़ाइबर (n-1) और फ़ाइबर (n-2) में तोड़ते हैं और दोनों भागों की गणना करते हैं। Iteration केवल आपको एक ही फ़ंक्शन को बार-बार दोहराने की अनुमति देता है।

हालांकि, फाइबोनैचि वास्तव में एक टूटा हुआ उदाहरण है और मुझे लगता है कि पुनरावृत्ति वास्तव में अधिक कुशल है। ध्यान दें कि फ़ाइबर (n) = फ़ाइबर (n-1) + फ़ाइबर (n-2) और फ़ाइबर (n-1) = फ़ाइबर (n-2) + फ़ाइबर (n-3)। फाइब (n-1) की गणना दो बार की जाती है!

एक बेहतर उदाहरण एक पेड़ के लिए एक पुनरावर्ती एल्गोरिदम है। माता-पिता के नोड के विश्लेषण की समस्या को प्रत्येक बच्चे के नोड के विश्लेषण की कई छोटी समस्याओं में तोड़ा जा सकता है। फाइबोनैचि उदाहरण के विपरीत, छोटी समस्याएं एक-दूसरे से स्वतंत्र होती हैं।

तो हाँ - पुनरावृत्ति उन समस्याओं के लिए पुनरावृत्ति से बेहतर है जिन्हें कई, छोटी, स्वतंत्र, समान समस्याओं में तोड़ा जा सकता है।


1
दो बार गणना वास्तव में ज्ञापन के माध्यम से बचा जा सकता है।
सिद्धार्थ

7

पुनरावृत्ति का उपयोग करते समय आपका प्रदर्शन बिगड़ जाता है क्योंकि किसी भी भाषा में एक विधि को कॉल करने से बहुत तैयारी होती है: कॉलिंग कोड एक रिटर्न एड्रेस, कॉल पैरामीटर, कुछ अन्य संदर्भ जानकारी जैसे प्रोसेसर रजिस्टरों को कहीं और बचाया जा सकता है, और वापसी के समय पर पोस्ट करता है विधि कहा जाता है एक वापसी मूल्य जो फिर फोन करने वाले द्वारा पुनर्प्राप्त किया जाता है, और किसी भी संदर्भ की जानकारी जो पहले सहेजी गई थी, उसे बहाल किया जाएगा। प्रदर्शन एक पुनरावृत्त और एक पुनरावर्ती दृष्टिकोण के बीच भिन्न होता है, जब ये ऑपरेशन होते हैं।

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


हमेशा सच नहीं है। पुनरावृत्ति कुछ मामलों के लिए पुनरावृत्ति के रूप में कुशल हो सकती है जहां पूंछ कॉल अनुकूलन किया जा सकता है। stackoverflow.com/questions/310974/…
सिड क्षत्रिय '

6

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

http://lambda-the-ultimate.org/node/1333


जावा के लिए जेवीएम हैं जो पूंछ-पुनरावृत्ति का अनुकूलन करते हैं। ibm.com/developerworks/java/library/j-diag8.html
Liran Orevi

5

ऐसे कई मामले हैं जहां यह पुनरावृत्त विधि पर बहुत अधिक सुरुचिपूर्ण समाधान देता है, सामान्य उदाहरण एक द्विआधारी पेड़ का ट्रैवर्सल है, इसलिए इसे बनाए रखना अधिक कठिन नहीं है। सामान्य तौर पर, पुनरावृत्त संस्करण आमतौर पर थोड़ा तेज़ होते हैं (और अनुकूलन के दौरान पुनरावर्ती संस्करण को अच्छी तरह से प्रतिस्थापित किया जा सकता है), लेकिन पुनरावर्ती संस्करण सही तरीके से समझने और लागू करने के लिए सरल होते हैं।


5

कुछ स्थितियों में पुनरावृत्ति बहुत उपयोगी है। उदाहरण के लिए भाज्य खोजने के लिए कोड पर विचार करें

int factorial ( int input )
{
  int x, fact = 1;
  for ( x = input; x > 1; x--)
     fact *= x;
  return fact;
}

अब इसे पुनरावर्ती फ़ंक्शन का उपयोग करके विचार करें

int factorial ( int input )
{
  if (input == 0)
  {
     return 1;
  }
  return input * factorial(input - 1);
}

इन दोनों का अवलोकन करके, हम देख सकते हैं कि पुनरावृत्ति को समझना आसान है। लेकिन अगर इसका उपयोग सावधानी से न किया जाए तो यह बहुत अधिक त्रुटि वाला भी हो सकता है। मान लीजिए अगर हम चूक जाते हैं if (input == 0), तो कोड को कुछ समय के लिए निष्पादित किया जाएगा और आमतौर पर स्टैक ओवरफ्लो के साथ समाप्त होगा।


6
मैं वास्तव में समझने के लिए पुनरावृत्त संस्करण आसान लगता है। प्रत्येक अपने स्वयं को, मुझे लगता है।
मैक्सएम

@Maxpm, एक उच्च आदेश पुनरावर्ती समाधान बहुत बेहतर है:, foldl (*) 1 [1..n]यह बात है।
तर्क

5

कई मामलों में कैशिंग के कारण पुनरावृत्ति तेज होती है, जिससे प्रदर्शन में सुधार होता है। उदाहरण के लिए, यहां पारंपरिक मर्ज दिनचर्या का उपयोग करके मर्ज प्रकार का पुनरावृत्त संस्करण है। यह कैशिंग बेहतर प्रदर्शन के कारण पुनरावर्ती कार्यान्वयन की तुलना में धीमी गति से चलेगा।

Iterative कार्यान्वयन

public static void sort(Comparable[] a)
{
    int N = a.length;
    aux = new Comparable[N];
    for (int sz = 1; sz < N; sz = sz+sz)
        for (int lo = 0; lo < N-sz; lo += sz+sz)
            merge(a, lo, lo+sz-1, Math.min(lo+sz+sz-1, N-1));
}

पुनरावर्ती कार्यान्वयन

private static void sort(Comparable[] a, Comparable[] aux, int lo, int hi)
{
    if (hi <= lo) return;
    int mid = lo + (hi - lo) / 2;
    sort(a, aux, lo, mid);
    sort(a, aux, mid+1, hi);
    merge(a, aux, lo, mid, hi);
}

PS - यह प्रोफेसर केविन वेन (प्रिंसटन यूनिवर्सिटी) द्वारा कौरसेरा पर प्रस्तुत एल्गोरिदम पर पाठ्यक्रम में बताया गया था।


4

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


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

4

पुनरावृत्ति और पुनरावृत्ति उस व्यावसायिक तर्क पर निर्भर करती है जिसे आप लागू करना चाहते हैं, हालांकि अधिकांश मामलों में इसका उपयोग पारस्परिक रूप से किया जा सकता है। अधिकांश डेवलपर्स पुनरावृत्ति के लिए जाते हैं क्योंकि यह समझना आसान है।


4

यह भाषा पर निर्भर करता है। जावा में आपको लूप का उपयोग करना चाहिए। कार्यात्मक भाषाएँ पुनरावर्तन का अनुकूलन करती हैं।


3

यदि आप किसी सूची से अधिक पुनरावृति कर रहे हैं, तो निश्चित रूप से, इसे पुन: व्यवस्थित करें।

अन्य उत्तरों के एक जोड़े ने (गहराई-पहले) ट्री ट्रैवरल का उल्लेख किया है। यह वास्तव में इस तरह के एक महान उदाहरण है, क्योंकि यह एक बहुत ही आम डेटा संरचना के लिए एक बहुत ही आम बात है। इस समस्या के लिए पुनरावृत्ति अत्यंत सहज है।

यहां "खोजें" विधियों की जांच करें: http://penguin.ewu.edu/cscd300/Topic/BSTintro/index.html


3

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

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


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

2

मुझे लगता है कि (नॉन टेल) रिकर्सन में हर बार फंक्शन (कोर्स की भाषा पर निर्भर) कहे जाने वाले नए स्टैक आदि के आवंटन के लिए एक प्रदर्शन हिट होगा।


2

यह "पुनरावृत्ति गहराई" पर निर्भर करता है। यह निर्भर करता है कि फ़ंक्शन कॉल ओवरहेड कुल निष्पादन समय को कितना प्रभावित करेगा।

उदाहरण के लिए, पुनरावर्ती तरीके से शास्त्रीय तथ्यात्मक गणना करना बहुत ही अक्षम होने के कारण है: - डेटा ओवरफ्लो होने का जोखिम - स्टैक ओवरफ्लो होने का जोखिम - फ़ंक्शन कॉल ओवरहेड पर कब्जा समय का 80% होता है

शतरंज के खेल में स्थिति विश्लेषण के लिए एक न्यूनतम-अधिकतम एल्गोरिथ्म विकसित करते समय, जो बाद के एन चालों का विश्लेषण करेगा "विश्लेषण गहराई" पर पुनरावृत्ति में लागू किया जा सकता है (जैसा कि मैं ^ _ ^ कर रहा हूं)


पूरी तरह से यहाँ ugasoft से सहमत हैं ... यह पुनरावृत्ति-गहराई पर निर्भर करता है .... और इसके पुनरावृत्ति कार्यान्वयन की जटिलता ... आपको दोनों की तुलना करने और यह देखने की आवश्यकता है कि कौन अधिक कुशल है ... ऐसा कोई भी नियम नहीं है। ..
राज वर्धन

2

प्रत्यावर्तन? मैं कहां से शुरू करूंगा, विकी आपको बताएगा "यह स्व-समान तरीके से आइटम दोहराने की प्रक्रिया है"

वापस दिन में जब मैं सी कर रहा था, सी + + रिकर्सन एक भगवान का भेजा हुआ सामान था, जैसे "टेल रिकर्सियन"। आपको कई छंटाई वाले एल्गोरिदम भी पुनरावृत्ति का उपयोग करते हुए मिलेंगे। त्वरित प्रकार का उदाहरण: http://alienryderflex.com/quicksort/

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


मुझे लगता है कि आपको कंपाइलर ऑप्टिमाइज़ेशन पिछड़ा हुआ मिला है। स्टैक विकास से बचने के लिए जब संभव हो तो कंपाइलर पुनरावर्ती कार्यों को पुनरावृत्त लूप में अनुकूलित करेगा।
कोडरडेन

उचित बिंदु, यह पीछे की ओर था। हालाँकि मुझे यकीन नहीं है कि यह अभी भी पूंछ पुनरावृत्ति के लिए लागू है।
निकज़

2

C ++ में यदि रिकर्सिव फ़ंक्शन एक टेम्प्लेटेड है, तो कंपाइलर के पास इसे ऑप्टिमाइज़ करने का अधिक मौका होता है, क्योंकि सभी प्रकार की कटौती और फ़ंक्शन इंस्टेंटिएशन संकलन समय में होंगे। यदि संभव हो तो आधुनिक कंपाइलर फ़ंक्शन को इनलाइन भी कर सकते हैं। तो एक का उपयोग करता है अनुकूलन झंडे की तरह करता है, तो -O3या -O2में g++है, तो recursions तेजी से पुनरावृत्तियों से बनने का मौका हो सकता है। पुनरावृत्त कोडों में, संकलक को इसे अनुकूलित करने का कम मौका मिलता है, क्योंकि यह पहले से ही अधिक या कम इष्टतम स्थिति में है (यदि अच्छी तरह से लिखा गया है)।

मेरे मामले में, मैं पुनरावर्ती और पुनरावृत्त तरीके से, आर्मडिलो मैट्रिक्स ऑब्जेक्ट्स का उपयोग करके मैट्रिक्स एक्सप्रेशन को लागू करने की कोशिश कर रहा था। एल्गोरिथ्म यहां पाया जा सकता है ... https://en.wikipedia.org/wiki/Exponentiation_by_squaring । मेरे कार्यों को समाप्त कर दिया गया था और मैंने 1,000,000 12x12सत्ता के लिए उठाए गए गणना की है 10। मुझे निम्नलिखित परिणाम मिला:

iterative + optimisation flag -O3 -> 2.79.. sec
recursive + optimisation flag -O3 -> 1.32.. sec

iterative + No-optimisation flag  -> 2.83.. sec
recursive + No-optimisation flag  -> 4.15.. sec

ये परिणाम -std=c++11इंटेल ++ के साथ c ++ 11 ध्वज ( ) और आर्मडिलो 6.1 के साथ gcc-4.8 का उपयोग करके प्राप्त किए गए हैं। इंटेल कंपाइलर भी इसी तरह के परिणाम दिखाता है।


1

माइक सही है। पूंछ पुनरावृत्ति जावा संकलक या जेवीएम द्वारा अनुकूलित नहीं है । आपको हमेशा कुछ इस तरह से स्टैक ओवरफ्लो मिलेगा:

int count(int i) {
  return i >= 100000000 ? i : count(i+1);
}

3
जब तक आप इसे स्काला ;-) में नहीं लिखते हैं
बेन हार्डी

1

आपको यह ध्यान रखना होगा कि ढेर ढेर के उपयोग के आधार पर आप बहुत गहरी पुनरावृत्ति का उपयोग करेंगे। इसे रोकने के लिए कुछ आधार मामले प्रदान करना सुनिश्चित करें जिससे आप पुनरावृत्ति को समाप्त करते हैं।


1

रिकर्सियन का एक नुकसान है कि आप जिस एल्गोरिदम को रिकर्सन का उपयोग करके लिखते हैं, उसमें O (n) स्पेस की जटिलता है। जबकि पुनरावृत्त aproach में O (1) की एक अंतरिक्ष जटिलता है। यह पुनरावृत्ति पर पुनरावृत्ति का उपयोग करने का प्रायोगिक है। फिर हम पुनरावर्तन का उपयोग क्यों करते हैं?

निचे देखो।

कभी-कभी पुनरावृत्ति का उपयोग करके एल्गोरिथ्म लिखना आसान होता है, जबकि पुनरावृत्ति का उपयोग करके एक ही एल्गोरिदम लिखना थोड़ा कठिन होता है। इस मामले में यदि आप पुनरावृत्ति दृष्टिकोण का पालन करना चुनते हैं तो आपको खुद को स्टैक को संभालना होगा।


1

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

उदाहरण के लिए, कुछ भाषाओं में, पुनरावर्ती मल्टीथ्रेडेड मर्ज सॉर्ट कार्यान्वयन हैं।

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


0

जहां तक ​​मुझे पता है, पर्ल टेल-रीकर्सिव कॉल को ऑप्टिमाइज़ नहीं करता है, लेकिन आप इसे नकली कर सकते हैं।

sub f{
  my($l,$r) = @_;

  if( $l >= $r ){
    return $l;
  } else {

    # return f( $l+1, $r );

    @_ = ( $l+1, $r );
    goto &f;

  }
}

जब पहली बार कहा जाता है तो यह स्टैक पर जगह आवंटित करेगा। फिर यह अपने तर्कों को बदल देगा, और सबरूटीन को फिर से शुरू करेगा, बिना स्टैक के कुछ और जोड़कर। इसलिए यह दिखावा करेगा कि इसने इसे स्वयंभू नहीं कहा, इसे एक पुनरावृति प्रक्रिया में बदल दिया।

ध्यान दें कि कोई " my @_;" या " local @_;" नहीं है, अगर आपने किया तो वह काम नहीं करेगा।


0

सिर्फ क्रोम 45.0.2454.85 मीटर का उपयोग करते हुए, पुनरावृत्ति तेजी से एक अच्छी राशि लगती है।

यहाँ कोड है:

(function recursionVsForLoop(global) {
    "use strict";

    // Perf test
    function perfTest() {}

    perfTest.prototype.do = function(ns, fn) {
        console.time(ns);
        fn();
        console.timeEnd(ns);
    };

    // Recursion method
    (function recur() {
        var count = 0;
        global.recurFn = function recurFn(fn, cycles) {
            fn();
            count = count + 1;
            if (count !== cycles) recurFn(fn, cycles);
        };
    })();

    // Looped method
    function loopFn(fn, cycles) {
        for (var i = 0; i < cycles; i++) {
            fn();
        }
    }

    // Tests
    var curTest = new perfTest(),
        testsToRun = 100;

    curTest.do('recursion', function() {
        recurFn(function() {
            console.log('a recur run.');
        }, testsToRun);
    });

    curTest.do('loop', function() {
        loopFn(function() {
            console.log('a loop run.');
        }, testsToRun);
    });

})(window);

परिणाम

// 100 लूप के लिए मानक का उपयोग करते हुए

लूप रन के लिए 100x। पूरा करने का समय: 7.683ms

// 100 रन कार्यात्मक पुनरावर्ती दृष्टिकोण w / पूंछ पुनरावृत्ति का उपयोग करते हुए

100x पुनरावर्तन रन। पूरा होने का समय: 4.841ms

नीचे दिए गए स्क्रीनशॉट में, प्रति चक्र 300 चक्रों पर चलने पर पुन: पुनरावृत्ति बड़े अंतर से जीतता है

फिर से मिली जीत!


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

0

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

संक्षेप में: 1) पुनरावृति पश्च-क्रम अनुक्रमण आसान नहीं है - जो डीएफटी को और अधिक जटिल बनाता है 2) पुनरावृत्ति के साथ चक्रों को आसान बनाता है

विवरण:

पुनरावर्ती मामले में, पूर्व और पोस्ट ट्रैवर्सल्स बनाना आसान है:

एक सुंदर मानक प्रश्न की कल्पना करें: "सभी कार्यों को प्रिंट करें जिन्हें कार्य 5 को निष्पादित करने के लिए निष्पादित किया जाना चाहिए, जब कार्य अन्य कार्यों पर निर्भर करते हैं"

उदाहरण:

    //key-task, value-list of tasks the key task depends on
    //"adjacency map":
    Map<Integer, List<Integer>> tasksMap = new HashMap<>();
    tasksMap.put(0, new ArrayList<>());
    tasksMap.put(1, new ArrayList<>());

    List<Integer> t2 = new ArrayList<>();
    t2.add(0);
    t2.add(1);
    tasksMap.put(2, t2);

    List<Integer> t3 = new ArrayList<>();
    t3.add(2);
    t3.add(10);
    tasksMap.put(3, t3);

    List<Integer> t4 = new ArrayList<>();
    t4.add(3);
    tasksMap.put(4, t4);

    List<Integer> t5 = new ArrayList<>();
    t5.add(3);
    tasksMap.put(5, t5);

    tasksMap.put(6, new ArrayList<>());
    tasksMap.put(7, new ArrayList<>());

    List<Integer> t8 = new ArrayList<>();
    t8.add(5);
    tasksMap.put(8, t8);

    List<Integer> t9 = new ArrayList<>();
    t9.add(4);
    tasksMap.put(9, t9);

    tasksMap.put(10, new ArrayList<>());

    //task to analyze:
    int task = 5;


    List<Integer> res11 = getTasksInOrderDftReqPostOrder(tasksMap, task);
    System.out.println(res11);**//note, no reverse required**

    List<Integer> res12 = getTasksInOrderDftReqPreOrder(tasksMap, task);
    Collections.reverse(res12);//note reverse!
    System.out.println(res12);

    private static List<Integer> getTasksInOrderDftReqPreOrder(Map<Integer, List<Integer>> tasksMap, int task) {
         List<Integer> result = new ArrayList<>();
         Set<Integer> visited = new HashSet<>();
         reqPreOrder(tasksMap,task,result, visited);
         return result;
    }

private static void reqPreOrder(Map<Integer, List<Integer>> tasksMap, int task, List<Integer> result, Set<Integer> visited) {

    if(!visited.contains(task)) {
        visited.add(task);
        result.add(task);//pre order!
        List<Integer> children = tasksMap.get(task);
        if (children != null && children.size() > 0) {
            for (Integer child : children) {
                reqPreOrder(tasksMap,child,result, visited);
            }
        }
    }
}

private static List<Integer> getTasksInOrderDftReqPostOrder(Map<Integer, List<Integer>> tasksMap, int task) {
    List<Integer> result = new ArrayList<>();
    Set<Integer> visited = new HashSet<>();
    reqPostOrder(tasksMap,task,result, visited);
    return result;
}

private static void reqPostOrder(Map<Integer, List<Integer>> tasksMap, int task, List<Integer> result, Set<Integer> visited) {
    if(!visited.contains(task)) {
        visited.add(task);
        List<Integer> children = tasksMap.get(task);
        if (children != null && children.size() > 0) {
            for (Integer child : children) {
                reqPostOrder(tasksMap,child,result, visited);
            }
        }
        result.add(task);//post order!
    }
}

ध्यान दें कि पुनरावर्ती पोस्ट-ऑर्डर-ट्रैवर्सल को परिणाम के बाद के उलट की आवश्यकता नहीं होती है। पिछले छपे प्रश्न में बच्चे पहले और आपके टास्क छपे। सब कुछ ठीक है। आप एक पुनरावर्ती पूर्व-आदेश-ट्रैवर्सल (जो ऊपर भी दिखाया गया है) कर सकते हैं और किसी को परिणाम सूची के उलटने की आवश्यकता होगी।

पुनरावृत्ति दृष्टिकोण के साथ इतना आसान नहीं है! पुनरावृत्ति (एक स्टैक) दृष्टिकोण में आप केवल एक पूर्व-आदेश-ट्रैवर्सल कर सकते हैं, इसलिए आप अंत में परिणाम सरणी को उलटने के लिए बाध्य हैं:

    List<Integer> res1 = getTasksInOrderDftStack(tasksMap, task);
    Collections.reverse(res1);//note reverse!
    System.out.println(res1);

    private static List<Integer> getTasksInOrderDftStack(Map<Integer, List<Integer>> tasksMap, int task) {
    List<Integer> result = new ArrayList<>();
    Set<Integer> visited = new HashSet<>();
    Stack<Integer> st = new Stack<>();


    st.add(task);
    visited.add(task);

    while(!st.isEmpty()){
        Integer node = st.pop();
        List<Integer> children = tasksMap.get(node);
        result.add(node);
        if(children!=null && children.size() > 0){
            for(Integer child:children){
                if(!visited.contains(child)){
                    st.add(child);
                    visited.add(child);
                }
            }
        }
        //If you put it here - it does not matter - it is anyway a pre-order
        //result.add(node);
    }
    return result;
}

सरल लगता है, नहीं?

लेकिन यह कुछ साक्षात्कारों में एक जाल है।

इसका अर्थ निम्नलिखित है: पुनरावर्ती दृष्टिकोण के साथ, आप गहराई पहले ट्रैवर्सल को लागू कर सकते हैं और फिर चयन कर सकते हैं कि आपको किस क्रम में पूर्व या पोस्ट की आवश्यकता है (बस "प्रिंट" के स्थान को बदलकर, "परिणाम सूची में जोड़ने" के हमारे मामले में) )। पुनरावृत्ति (एक स्टैक) दृष्टिकोण के साथ आप आसानी से केवल पूर्व-क्रम ट्रैवर्सल कर सकते हैं और इसलिए उस स्थिति में जब बच्चों को पहले मुद्रित करने की आवश्यकता होती है (बहुत सारी स्थिति जब आपको नीचे के नोड्स से प्रिंट शुरू करने की आवश्यकता होती है, ऊपर की तरफ जा रहे हैं) - आप में हैं मुसीबत। यदि आपके पास वह परेशानी है जो आप बाद में उलट सकते हैं, लेकिन यह आपके एल्गोरिथ्म के लिए एक अतिरिक्त होगा। और अगर एक साक्षात्कारकर्ता अपनी घड़ी को देख रहा है तो यह आपके लिए एक समस्या हो सकती है। पुनरावृत्ति के बाद के ट्रैवर्सल के जटिल तरीके हैं, वे मौजूद हैं, लेकिन वे सरल नहीं हैं । उदाहरण:https://www.geeksforgeeks.org/iterative-postorder-traversal-using-stack/

इस प्रकार, निचला रेखा: मैं साक्षात्कार के दौरान पुनरावृत्ति का उपयोग करूंगा, इसे प्रबंधित करना और व्याख्या करना सरल है। आपके पास किसी भी जरूरी मामले में प्री-ऑर्डर से पहले आने-जाने का आसान तरीका है। पुनरावृत्ति के साथ आप लचीले नहीं हैं।

मैं पुनरावृत्ति का उपयोग करता हूं और फिर बताता हूं: "ठीक है, लेकिन पुनरावृत्त मुझे इस्तेमाल की गई स्मृति पर अधिक प्रत्यक्ष नियंत्रण प्रदान कर सकता है, मैं आसानी से स्टैक आकार को माप सकता हूं और कुछ खतरनाक अतिप्रवाह को हटा सकता हूं .."

पुनरावृत्ति का एक और प्लस - यह एक ग्राफ में चक्र से बचने / नोटिस करने के लिए सरल है।

उदाहरण (प्रुडोकोड):

dft(n){
    mark(n)
    for(child: n.children){
        if(marked(child)) 
            explode - cycle found!!!
        dft(child)
    }
    unmark(n)
}

0

इसे पुनरावृत्ति के रूप में, या अभ्यास के रूप में लिखना मजेदार हो सकता है।

हालांकि, यदि कोड का उपयोग उत्पादन में किया जाना है, तो आपको स्टैक ओवरफ्लो की संभावना पर विचार करना होगा।

टेल रिकर्सन ऑप्टिमाइज़ेशन स्टैक ओवरफ़्लो को समाप्त कर सकता है, लेकिन क्या आप इसे बनाने की परेशानी से गुज़रना चाहते हैं, और आपको यह जानना होगा कि आप अपने वातावरण में ऑप्टिमाइज़ेशन को गिन सकते हैं।

हर बार अल्गोरिदम की पुनरावृत्ति होती है, डेटा का आकार कितना या उससे nकम होता है?

यदि आप डेटा के आकार को कम कर रहे हैं या nहर बार जब आप फिर से उठते हैं, तो सामान्य तौर पर आपको स्टैक ओवरफ्लो के बारे में चिंता करने की आवश्यकता नहीं है। कहो, अगर यह अतिप्रवाह ढेर करने के लिए कार्यक्रम के लिए 4,000 स्तर गहरा या 10,000 स्तर गहरा होने की आवश्यकता है, तो आपके कार्यक्रम को ओवरफ्लो ढेर करने के लिए आपके डेटा का आकार लगभग 2 4000 होना चाहिए । उस परिप्रेक्ष्य में रखने के लिए, हाल ही में एक सबसे बड़ा भंडारण उपकरण 2 61 बाइट पकड़ सकता है , और यदि आपके पास ऐसे उपकरणों में से 2 61 हैं, तो आप केवल 2 122 डेटा आकार के साथ काम कर रहे हैं । यदि आप ब्रह्मांड के सभी परमाणुओं को देख रहे हैं, तो अनुमान है कि यह 2 से कम हो सकता है 84। यदि आपको ब्रह्मांड के सभी डेटा और उनके राज्यों से प्रत्येक मिलीसेकंड के लिए निपटने की आवश्यकता है क्योंकि ब्रह्मांड का जन्म 14 अरब साल पहले होने का अनुमान है, तो यह केवल 2 153 हो सकता है । इसलिए यदि आपका प्रोग्राम 2 4000 यूनिट डेटा nको संभाल सकता है या , आप ब्रह्मांड के सभी डेटा को संभाल सकते हैं और प्रोग्राम ओवरफ्लो नहीं करेगा। यदि आपको उन संख्याओं से निपटने की आवश्यकता नहीं है जो 2 4000 (4000-बिट पूर्णांक) जितनी बड़ी हैं , तो सामान्य तौर पर आपको स्टैक ओवरफ्लो के बारे में चिंता करने की आवश्यकता नहीं है।

हालांकि, अगर आप डेटा के आकार को कम या nहर बार जब आप recurse एक निरंतर राशि से है, तो आप ढेर अतिप्रवाह में जब अपने कार्यक्रम अच्छी तरह से चलता है जब चला सकते nहै 1000लेकिन कुछ स्थिति में, जब nकेवल हो जाता है 20000

इसलिए यदि आपके पास स्टैक ओवरफ्लो की संभावना है, तो इसे एक पुनरावृत्त समाधान बनाने का प्रयास करें।


-1

मैं "इंडक्शन" द्वारा हास्केल डेटा संरचना को डिज़ाइन करके आपके प्रश्न का उत्तर देने जा रहा हूं, जो पुनरावृत्ति के लिए "दोहरी" का एक प्रकार है। और फिर मैं दिखाऊंगा कि कैसे यह द्वंद्व अच्छी चीजों की ओर ले जाता है।

हम एक साधारण पेड़ के लिए एक प्रकार का परिचय देते हैं:

data Tree a = Branch (Tree a) (Tree a)
            | Leaf a
            deriving (Eq)

हम इस परिभाषा को यह कहते हुए पढ़ सकते हैं कि "एक पेड़ एक शाखा है (जिसमें दो पेड़ होते हैं) या एक पत्ती (जिसमें डेटा मान होता है)"। तो पत्ती एक प्रकार का न्यूनतम मामला है। यदि एक पेड़ एक पत्ती नहीं है, तो यह एक मिश्रित पेड़ होना चाहिए जिसमें दो पेड़ हों। ये ही मामले हैं।

चलो एक पेड़ बनाते हैं:

example :: Tree Int
example = Branch (Leaf 1) 
                 (Branch (Leaf 2) 
                         (Leaf 3))

अब, मान लें कि हम पेड़ में प्रत्येक मूल्य में 1 जोड़ना चाहते हैं। हम इसे कॉल करके कर सकते हैं:

addOne :: Tree Int -> Tree Int
addOne (Branch a b) = Branch (addOne a) (addOne b)
addOne (Leaf a)     = Leaf (a + 1)

सबसे पहले, ध्यान दें कि यह वास्तव में एक पुनरावर्ती परिभाषा है। यह डेटा कंस्ट्रक्टर्स ब्रांच और लीफ को मामलों के रूप में लेता है (और चूंकि पत्ता न्यूनतम है और ये केवल संभावित मामले हैं), हमें यकीन है कि फ़ंक्शन समाप्त हो जाएगा।

AddOne को iterative स्टाइल में लिखने में क्या लगेगा? शाखाओं की एक मनमानी संख्या में लूपिंग कैसा दिखेगा?

इसके अलावा, इस तरह की पुनरावृत्ति को अक्सर "फ़ाइटर" के रूप में फैक्टर किया जा सकता है। हम पेड़ों को फफूंद लगाकर फफूंद लगा सकते हैं:

instance Functor Tree where fmap f (Leaf a)     = Leaf (f a)
                            fmap f (Branch a b) = Branch (fmap f a) (fmap f b)

और परिभाषित करना:

addOne' = fmap (+1)

हम बीजगणितीय डेटा प्रकार के लिए अन्य पुनरावर्तन योजनाएं जैसे कि कायापलट (या गुना) को समाप्त कर सकते हैं। एक कायापलट का उपयोग करते हुए, हम लिख सकते हैं:

addOne'' = cata go where
           go (Leaf a) = Leaf (a + 1)
           go (Branch a b) = Branch a b

-2

स्टैक ओवरफ्लो तभी होगा जब आप किसी ऐसी भाषा में प्रोग्रामिंग कर रहे हों जिसमें अंतर्निहित मेमोरी प्रबंधन नहीं है .... अन्यथा, सुनिश्चित करें कि आपके पास अपने फ़ंक्शन (या फ़ंक्शन कॉल, STDLbs, आदि) में कुछ है। पुनरावृत्ति के बिना यह संभव नहीं होगा कि चीजें हों ... Google या SQL, या किसी भी स्थान को बड़ी डेटा संरचनाओं (कक्षाओं) या डेटाबेस के माध्यम से कुशलतापूर्वक सॉर्ट करना होगा।

पुनरावृत्ति जाने के लिए रास्ता है अगर आप फ़ाइलों के माध्यम से पुनरावृति करना चाहते हैं, तो यकीन है कि यह कैसे * * खोजें | ? grep * 'काम करता है। Kinda दोहरी पुनरावृत्ति, विशेष रूप से पाइप के साथ (लेकिन syscalls का एक गुच्छा ऐसा नहीं करते हैं जैसे कि अगर यह ऐसा कुछ है जो आप दूसरों को उपयोग करने के लिए बाहर रखने जा रहे हैं)।

उच्च स्तर की भाषाएं और यहां तक ​​कि क्लैंग / सीपीपी भी पृष्ठभूमि में इसे लागू कर सकते हैं।


1
"स्टैक ओवरफ्लो तभी होगा जब आप किसी ऐसी भाषा में प्रोग्रामिंग कर रहे हों जिसमें अंतर्निहित मेमोरी प्रबंधन नहीं है" - कोई मतलब नहीं है। अधिकांश भाषाएं सीमित आकार के ढेर का उपयोग करती हैं, इसलिए पुनरावृत्ति बहुत जल्द एक विफलता का कारण बनेगी।
स्टेसीगर्ल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.