क्या एक समय लूप आंतरिक रूप से एक पुनरावृत्ति है?


37

मैंने सोचा कि क्या एक समय पाश आंतरिक रूप से एक पुनरावृत्ति है?

मुझे लगता है कि यह इसलिए है क्योंकि थोड़ी देर के लूप को एक फ़ंक्शन के रूप में देखा जा सकता है जो अंत में खुद को कॉल करता है। यदि यह पुनरावृत्ति नहीं है, तो क्या अंतर है?



13
आप पुनरावृत्ति को पुनरावृति में बदल सकते हैं और इसके विपरीत, हाँ। इसका मतलब यह नहीं है कि वे एक ही हैं, वे सिर्फ एक ही क्षमता है। ऐसे समय होते हैं जब पुनरावृत्ति अधिक प्राकृतिक होती है, और ऐसे समय होते हैं जहां पुनरावृत्ति अधिक प्राकृतिक होती है।
पॉलिग्नोम जूल

18
@MingDuck आप प्रेरण से साबित कर सकते हैं कि किसी भी पुनरावृत्ति को पुनरावृत्ति और इसके विपरीत लिखा जा सकता है। हां, यह बहुत अलग दिखाई देगा, लेकिन आप इसे फिर भी कर सकते हैं।
पॉलिग्नोम जूल

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

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

जवाबों:


116

लूप बहुत अधिक नहीं हैं । वास्तव में, वे विपरीत तंत्र के प्रमुख उदाहरण हैं : पुनरावृत्ति

पुनरावृत्ति की बात यह है कि प्रसंस्करण का एक तत्व स्वयं का एक और उदाहरण कहता है। लूप नियंत्रण तंत्र केवल उस बिंदु पर वापस कूदता है जहां यह शुरू हुआ था।

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

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


10
@Giorgio यह सच हो सकता है, लेकिन यह एक दावे पर एक टिप्पणी है जिसका जवाब नहीं दिया गया है। "मनमाने ढंग से" इस उत्तर में मौजूद नहीं है और इससे अर्थ में काफी बदलाव आएगा।
hvd

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

15
@Giorgio "यह वही है जो पुनरावृत्ति करता है: कॉल के लिए छोड़कर नए तर्क के साथ खुद को कॉल करें"। और तर्क।
hobbs

12
@ जियोर्जियो आप शब्दों की विभिन्न परिभाषाओं का उपयोग यहां से कर रहे हैं। शब्द, आप जानते हैं, संचार का आधार हैं। यह प्रोग्रामर है, सीएस स्टैक एक्सचेंज नहीं। यदि हमने "तर्क", "कॉल", "फ़ंक्शन" आदि जैसे शब्दों का उपयोग किया है, तो जिस तरह से आप सुझाव देते हैं, वास्तविक कोड के बारे में चर्चा करना असंभव होगा।
२५'१६ को

6
@ जियोर्जियो मैं अमूर्त अवधारणा को देख रहा हूं। वहाँ अवधारणा है जहाँ आप पुनरावृत्ति करते हैं और वह अवधारणा जहाँ आप लूप करते हैं। वे अलग अवधारणाएं हैं। हॉब्स 100% सही है कि कोई तर्क नहीं है और कोई कॉल नहीं है। वे मौलिक और अमूर्त रूप से भिन्न हैं। और यह अच्छा है क्योंकि वे विभिन्न समस्याओं को हल करते हैं। दूसरी ओर, आप देख रहे हैं कि जब आपका एकमात्र उपकरण पुनरावृत्ति होता है तो आप लूप को कैसे लागू कर सकते हैं। विडंबना यह है कि आप होब्स को कार्यान्वयन के बारे में सोचना बंद कर देंगे और अवधारणाओं को देखना शुरू कर देंगे, जब आपकी कार्यप्रणाली वह है जिसे वास्तव में पुनर्मूल्यांकन की आवश्यकता है।
corsiKa

37

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

या तो मामले में, लोग शायद आपको समझ नहीं पाएंगे अगर आप किसी फ़ंक्शन को पुनरावर्ती कहते हैं क्योंकि इसमें थोड़ी देर लूप होता है।

* हालांकि शायद अप्रत्यक्ष रूप से, उदाहरण के लिए यदि आप इसे परिभाषित करते हैं fold


4
निष्पक्ष होने के लिए, फ़ंक्शन किसी भी परिभाषा में पुनरावर्ती नहीं है। इसमें सिर्फ एक पुनरावर्ती तत्व, लूप शामिल है।
लुअन

@ लुअन: निश्चित रूप से ऐसा है, लेकिन चूंकि एक whileनिर्माण पुनरावृत्ति वाली भाषाओं में आम तौर पर कार्यों की एक संपत्ति होती है, इसलिए मैं इस संदर्भ में "पुनरावर्ती" के रूप में वर्णन करने के लिए कुछ और नहीं सोच सकता।
एंटन गोलोव जूल

36

यह आपकी बातों पर निर्भर करता है।

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

कई प्रोग्रामिंग भाषाएं पुनरावृत्ति और पुनरावृत्ति को समान नहीं मानती हैं, और अच्छे कारण के लिए। आमतौर पर , पुनरावृत्ति का अर्थ है कि भाषा / संकलक कॉल स्टैक को संभालता है, और पुनरावृत्ति का अर्थ है कि आपको स्टैक-हैंडलिंग स्वयं करना पड़ सकता है।

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

तो नहीं, वे आंतरिक रूप से समान नहीं हैं । वे समान रूप से अभिव्यंजक हैं , जिसका अर्थ है कि आप कुछ पुनरावृत्तियों की गणना नहीं कर सकते हैं आप पुनरावृत्ति और इसके विपरीत की गणना नहीं कर सकते हैं, लेकिन यह इसके बारे में है, सामान्य मामले में (चर्च-ट्यूरिंग थीसिस के अनुसार)।

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


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

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

यह जानना काफी महत्वपूर्ण है, क्योंकि यह वास्तविक दुनिया में काफी मौलिक अंतर पैदा कर सकता है। पुनरावृत्ति के लिए, पूरे संदर्भ को हर कॉल पर सहेजना होगा। पुनरावृत्ति के लिए, आपके पास सटीक नियंत्रण है कि चर स्मृति में क्या हैं और कहाँ सहेजे गए हैं।

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

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


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

3
जब तक भाषा पूरी तरह से ठीक हो रही है, तब तक यह हो सकता है। उदाहरण के लिए एक सरणी को आसानी से (दोगुनी) लिंक की गई सूची से बदला जा सकता है। यदि आप दो ट्यूरिंग-पूर्ण भाषाओं की तुलना करते हैं तो पुनरावृत्ति या पुनरावृत्ति और वेदर के बराबर होने की बात करना ही समझ में आता है।
पॉलिग्नोम जूल 25'16

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

1
यदि भाषा पूरी तरह से नहीं चल रही है, तो इसके साथ शुरू करने के लिए व्यर्थ है। DFA न तो मनमाना पुनरावृत्ति कर सकते हैं और न ही पुनरावर्तन btw।
बहुपत्नी

2
कोई भी कार्यान्वयन वास्तव में ट्यूरिंग-पूर्ण नहीं है, और जो भाषाएं ट्यूरिंग-पूर्ण नहीं हैं, वे कई उद्देश्यों के लिए उपयोगी हो सकती हैं। कोई भी प्रोग्राम जो एक परिमित श्रेणी के साथ एक परिमित संख्या में चर के साथ चलाया जा सकता है, एक DFA द्वारा समायोजित किया जा सकता है, जहां मूल्यों का हर संभव संयोजन एक असतत अवस्था है।
सुपरकैट

12

अंतर निहित ढेर और अर्थ है।

एक लूप जो "अंत में खुद को कॉल करता है" जब यह किया जाता है तो वापस क्रॉल करने के लिए कोई स्टैक नहीं होता है। यह अंतिम पुनरावृत्ति सेट है कि यह समाप्त होते ही क्या स्थिति होगी।

पुनरावृत्ति हालांकि इस अंतर्निहित स्टैक के बिना नहीं की जा सकती है जो पहले किए गए कार्य की स्थिति को याद करती है।

यह सही है कि आप पुनरावृत्ति के साथ किसी भी पुनरावृत्ति समस्या को हल कर सकते हैं यदि आप इसे स्पष्ट रूप से एक स्टैक तक पहुंच देते हैं। लेकिन ऐसा करना वैसा नहीं है।

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

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


5
मुझे लगता है कि "निहित स्टैक" भ्रामक है। पुनरावृत्ति एक भाषा के शब्दार्थ का हिस्सा है, न कि कार्यान्वयन विवरण। (दी गई, अधिकांश पुनरावर्तन-समर्थक भाषाएं कॉल स्टैक का उपयोग करती हैं; लेकिन सबसे पहले, ऐसी कुछ भाषाएं नहीं करती हैं, और दूसरी बात, यहां तक ​​कि भाषाओं में भी, जो प्रत्येक पुनरावर्ती कॉल आवश्यक रूप से कॉल स्टैक के लिए अपील नहीं करती हैं, क्योंकि कई भाषाएं अनुकूलन का समर्थन करती हैं। टेल कॉल एलिमिनेशन के रूप में ।) सामान्य / सरल कार्यान्वयन को समझना एब्सट्रैक्शन पर एक हैंडल प्राप्त करने में उपयोगी हो सकता है, लेकिन आपको यह पूरी कहानी है यह सोचकर खुद को चकरा नहीं देना चाहिए।
२५:१६

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

@ruakh: एकल पुनरावर्ती कॉल के भीतर किसी भी राज्य को एक अंतर्निहित स्टैक पर संग्रहीत किया जाएगा, जब तक कि पुनरावृत्ति को संकलक द्वारा पुनरावृत्त लूप में परिवर्तित नहीं किया जा सकता है। पूंछ कॉल उन्मूलन है एक कार्यान्वयन विस्तार, जिनमें आप अगर आप अपने समारोह पुनर्निर्माण करने के लिए पूंछ पुनरावर्ती होना चाहते हैं के बारे में पता होना चाहिए। इसके अलावा, "कुछ ऐसी भाषाएं नहीं" - क्या आप उन भाषाओं का उदाहरण प्रदान कर सकते हैं जिन्हें पुनरावर्ती कॉल के लिए स्टैक की आवश्यकता नहीं है?
Groo


@ruakh: CPS अपने आप में एक ही अंतर्निहित स्टैक बनाता है, इसलिए इसे समझ में आने के लिए टेल कॉल एलिमिनेशन पर भरोसा करना चाहिए (जो कि इसके निर्माण के कारण तुच्छ हो जाता है)। यहां तक ​​कि आपके द्वारा जोड़ा गया विकिपीडिया लेख भी यही कहता है: टेल कॉल ऑप्टिमाइज़ेशन (TCO) के बिना CPS का उपयोग करने से न केवल पुनरावृत्ति के दौरान संभावित रूप से विकसित होने के लिए निर्मित निरंतरता का कारण होगा, बल्कि कॉल स्टैक भी होगा
Groo

7

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

असली सवाल यह है: जब यह (लूप) और पुनरावृत्ति के बीच अंतर करने के लिए समझ में आता है, और जब यह समान चीजों के रूप में सोचने के लिए उपयोगी है? इसका उत्तर यह है कि जब वास्तव में प्रोग्रामिंग (गणितीय प्रमाण लिखने के लिए विरोध किया जाता है) तो पुनरावृत्ति और पुनरावृत्ति के बीच अंतर करना महत्वपूर्ण है।

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

टेल-कॉल पुनरावृत्ति (आमतौर पर कार्यात्मक भाषाओं) वाली भाषाओं में, कंपाइलर पुनरावर्ती कॉल को इस तरह से अनुकूलित करने में सक्षम हो सकता है कि वे केवल एक निरंतर मात्रा में मेमोरी लेते हैं। उन भाषाओं में महत्वपूर्ण अंतर पुनरावृत्ति बनाम पुनरावृत्ति नहीं है, बल्कि गैर-पूंछ-कॉल-पुनरावृत्ति संस्करण पूंछ-कॉल-पुनरावृत्ति और पुनरावृत्ति है।

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


3

whileलूप पुनरावृत्ति का एक रूप है, उदाहरण के लिए इस प्रश्न का स्वीकृत उत्तर देखें । वे कम्प्यूटेबिलिटी सिद्धांत में μ-ऑपरेटर के अनुरूप हैं (उदाहरण के लिए यहां देखें )।

forछोरों की सभी विविधताएं जो संख्याओं की एक सीमा पर परिमित करती हैं, एक परिमित संग्रह, एक सरणी, और इसी तरह, आदिम पुनरावृत्ति के अनुरूप हैं, उदाहरण के लिए यहां और यहां देखें । ध्यान दें कि forलूप्स C, C ++, जावा, और इसी तरह, वास्तव में एक whileलूप के लिए सिंटैक्टिक चीनी हैं , और इसलिए यह आदिम पुनरावृत्ति के अनुरूप नहीं है। पास्कल forलूप आदिम पुनरावृत्ति का एक उदाहरण है।

एक महत्वपूर्ण अंतर यह है कि आदिम पुनरावृत्ति हमेशा समाप्त होती है, जबकि सामान्यीकृत पुनरावर्तन ( whileलूप) समाप्त नहीं हो सकती है।

संपादित करें

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

क्या एक समय लूप आंतरिक रूप से एक पुनरावृत्ति है?

चूंकि आप whileस्वयं के संदर्भ में एक लूप को परिभाषित कर सकते हैं

while p do c := if p then (c; while p do c))

तो, हाँ , एक whileलूप पुनरावृत्ति का एक रूप है। पुनरावर्ती कार्य पुनरावर्तन का एक और रूप है (पुनरावर्ती परिभाषा का दूसरा उदाहरण)। सूची और पेड़ पुनरावृत्ति के अन्य रूप हैं।

एक और सवाल जो कई जवाबों और टिप्पणियों से निहित है

जबकि लूप और पुनरावर्ती कार्य समान हैं?

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

तो, यह तथ्य कि "एक whileलूप एक पुनरावृत्ति का रूप है" इस तथ्य का खंडन नहीं करता है कि "कुछ पुनरावर्ती कार्यों को whileलूप द्वारा व्यक्त नहीं किया जा सकता है "।


2
@morbidCode: आदिम पुनरावर्तन और μ-पुनर्संरचना विशिष्ट प्रतिबंधों (या इसके अभाव) के साथ पुनरावृत्ति के रूप हैं, जिनका अध्ययन संगणनीय सिद्धांत में किया जाता है। जैसा कि यह पता चलता है, सिर्फ एक FORलूप वाली भाषा बिल्कुल सभी आदिम पुनरावर्ती कार्यों की गणना कर सकती है, और सिर्फ एक WHILEलूप वाली भाषा बिल्कुल सभी ive-पुनरावर्ती कार्यों की गणना कर सकती है (और यह पता चलता है कि µ-पुनरावर्ती कार्य वास्तव में वे कार्य हैं ट्यूरिंग मशीन गणना कर सकती है)। या, इसे छोटा बनाने के लिए: आदिम पुनरावृत्ति और rec-पुनर्संरचना गणित / संगणना सिद्धांत से तकनीकी शब्द हैं।
जोर्ग डब्ल्यू मित्तग

2
मैंने सोचा कि "पुनरावृत्ति" ने एक फ़ंक्शन को खुद को कॉल करने के लिए प्रेरित किया, जिसके परिणामस्वरूप वर्तमान निष्पादन राज्य को स्टैक और इतने पर धकेल दिया गया; इसलिए अधिकांश मशीनों की एक व्यावहारिक सीमा होती है कि आप कितने स्तरों पर पुनरावृत्ति कर सकते हैं। हालांकि छोरों की ऐसी कोई सीमा नहीं होती है क्योंकि वे आंतरिक रूप से "JMP" की तरह कुछ का उपयोग करेंगे और स्टैक का उपयोग नहीं करेंगे। बस मेरी समझ, गलत हो सकती है।
जय

13
यह उत्तर "रिकर्सिव" शब्द के लिए पूरी तरह से अलग परिभाषा का उपयोग कर रहा है जो ओपी उपयोग कर रहा था, और इस प्रकार अत्यधिक भ्रामक है।
मूइंग डक

2
@DavidGrinberg: उद्धरण: "लूप के लिए C, C ++, जावा आदिम पुनरावृत्ति का उदाहरण नहीं हैं। आदिम पुनरावृत्ति का अर्थ है कि लूप शुरू करने से पहले पुनरावृत्तियों / पुनरावृत्ति की अधिकतम संख्या तय की गई है।" जियोर्जियो कम्प्यूटेबिलिटी सिद्धांत आदिम के बारे में बात कर रहा है । प्रोग्रामिंग भाषाओं से असंबंधित।
मूविंग डक

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

2

एक टेल कॉल (या टेल पुनरावर्ती कॉल) बिल्कुल "तर्कों के साथ गोटो" के रूप में लागू किया जाता है ( कॉल स्टैक पर किसी भी अतिरिक्त कॉल फ्रेम को धक्का दिए बिना ) और कुछ कार्यात्मक भाषाओं में (ओकेमेल विशेष रूप से) लूपिंग का सामान्य तरीका है।

इसलिए थोड़ी देर के लिए लूप (उन्हें होने वाली भाषाओं में) अपने शरीर (या उसके सिर के परीक्षण) के लिए एक पूंछ कॉल के साथ समाप्त होने के रूप में देखा जा सकता है।

इसी तरह, साधारण (नॉन टेल-कॉल) पुनरावर्ती कॉल को छोरों (कुछ स्टैक का उपयोग करके) द्वारा सिम्युलेटेड किया जा सकता है।

निरंतरता और निरंतरता-गुजरने की शैली के बारे में भी पढ़ें ।

तो "पुनरावृत्ति" और "पुनरावृत्ति" गहराई से बराबर हैं।


1

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

प्रोग्रामिंग के संदर्भ में मूलभूत अंतर यह है कि पुनरावृत्ति आपको कॉल स्टैक पर संग्रहीत डेटा का उपयोग करने की अनुमति देता है। इसे समझने के लिए, मान लें कि आप एक लूप या पुनरावृत्ति का उपयोग करके एक एकल-लिंक की गई सूची के तत्वों को प्रिंट करना चाहते हैं। मैं उदाहरण कोड के लिए C का उपयोग करूंगा:

 typedef struct List List;
 struct List
 {
     List* next;
     int element;
 };

 void print_list_loop(List* l)
 {
     List* it = l;
     while(it != NULL)
     {
          printf("Element: %d\n", it->element);
          it = it->next;
     }
 }

 void print_list_rec(List* l)
 {
      if(l == NULL) return;
      printf("Element: %d\n", l->element);
      print_list_rec(l->next);
 }

सरल, सही? अब एक मामूली बदलाव करते हैं: सूची को रिवर्स ऑर्डर में प्रिंट करें।

पुनरावर्ती संस्करण के लिए, यह मूल कार्य के लिए लगभग तुच्छ संशोधन है:

void print_list_reverse_rec(List* l)
{
    if (l == NULL) return;
    print_list_reverse_rec(l->next);
    printf("Element: %d\n", l->element);
}

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

इसलिए हमें या तो पूरी तरह से फिर से काम करना पड़ता है, या हमें एक सहायक डेटा संरचना का निर्माण करना पड़ता है, जो विज़िट किए गए तत्वों पर नज़र रखता है और जिससे हम फिर कुशलता से प्रिंट कर सकते हैं।

हमें पुनरावृत्ति के साथ यह समस्या क्यों नहीं है? क्योंकि पुनरावृत्ति में हमारे पास पहले से ही एक सहायक डेटा संरचना है: फ़ंक्शन कॉल स्टैक।

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


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

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

मेरा कहना था कि यदि आप इस समस्या में भाग लेते हैं, तो यह कार्यात्मक डिजाइन की कमी के लिए अधिक नीचे है, जिस भाषा का निर्माण आप इसे लागू करने के लिए करते हैं, और प्रत्येक विकल्प की अपनी सकारात्मकता और नकारात्मकता है :)
बाल्ड्रिक जूल

0

किसी विशेष कार्य (अधिकतर पुनरावृत्ति) को प्राप्त करने के लिए लूप पुनरावृत्ति का एक विशेष रूप है। एक कई प्रदर्शनों में एक ही प्रदर्शन [1] के साथ एक पुनरावर्ती शैली में एक लूप को लागू कर सकता है। और SICP [2] में, आप देख सकते हैं कि लूप्स को "सिंटैस्टिक शुगर" के रूप में वर्णित किया गया है। अधिकांश अनिवार्य प्रोग्रामिंग भाषाओं में, जबकि और ब्लॉक अपने मूल कार्य के समान स्कोप का उपयोग कर रहे हैं। बहरहाल, अधिकांश कार्यात्मक प्रोग्रामिंग भाषाओं में न तो है और न ही जबकि लूप मौजूद हैं क्योंकि उनकी कोई आवश्यकता नहीं है।

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

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

निम्नलिखित उदाहरण में, जीवन है फ़ंक्शन दो मापदंडों को लेता है "नियम" और "राज्य", और अगली बार टिक में नए राज्य का निर्माण किया जाएगा।

life rules state = life rules new_state
    where new_state = construct_state_in_time rules state

[१]: टेल कॉल ऑप्टिमाइज़ेशन, नई प्रोग्रामिंग बनाने के बजाय पुनरावर्ती कॉलों में मौजूदा फ़ंक्शन स्टैक का उपयोग करने के लिए कार्यात्मक प्रोग्रामिंग भाषाओं में एक सामान्य अनुकूलन है।

[२]: कंप्यूटर प्रोग्राम की संरचना और व्याख्या, MIT। https://mitpress.mit.edu/books/structure-and-interpretation-computer-programs


4
@ जियोर्जियो मेरा पतन नहीं है, लेकिन सिर्फ एक अनुमान है: मुझे लगता है कि अधिकांश प्रोग्रामर महसूस करते हैं, कि पुनरावृत्ति का अर्थ है एक पुनरावर्ती कार्य कॉल है, क्योंकि, ठीक है, यही पुनरावृत्ति है, एक फ़ंक्शन खुद को बुला रहा है। लूप में, कोई पुनरावर्ती फ़ंक्शन कॉल नहीं है। इसलिए यह कहना कि पुनरावर्ती फ़ंक्शन कॉल के बिना एक लूप पुनरावृत्ति का एक विशेष रूप है, इस परिभाषा के आधार पर, गलत तरीके से गलत होगा।
हाइड

1
ठीक है, शायद इसे और अधिक सार बिंदु से देख रहे हैं, जो अलग-अलग चीजें प्रतीत होती हैं, वास्तव में वैचारिक रूप से समान हैं। मुझे यह सोचकर बहुत हतोत्साहित और दुःख होता है कि लोग सिर्फ इसलिए जवाब दे देते हैं क्योंकि वे उन्हें कुछ सीखने का मौका देने के बजाय उनकी उम्मीदों के अनुरूप नहीं होते हैं। सभी उत्तर जो कहने की कोशिश करते हैं: "अरे, देखो, ये चीजें सतह पर अलग दिखती हैं, लेकिन वास्तव में एक समान स्तर पर समान हैं" को नीचा दिखाया गया है।
जियोर्जियो

3
@Georgio: इस साइट का उद्देश्य प्रश्नों के उत्तर प्राप्त करना है। ऐसे उत्तर जो सहायक और सही हैं, वे उत्थान के योग्य हैं, उत्तर जो भ्रमित करने वाले और अप्रभावी हैं, वे उत्थान के योग्य हैं। एक उत्तर जो सामान्य रूप से एक सामान्य शब्द की एक अलग परिभाषा का उपयोग करता है, यह स्पष्ट किए बिना कि क्या अलग परिभाषा का उपयोग किया जाता है भ्रामक और अनपेक्षित है। उत्तर जो केवल तभी समझ में आता है जब आप पहले से ही उत्तर जानते हैं, इसलिए बोलने के लिए, सहायक नहीं हैं, और केवल लेखकों को शब्दावली की बेहतर समझ दिखाने के लिए कार्य करता है।
जैक्सबी

2
@ जेसीबीएसबी: "उत्तर जो केवल तभी समझ में आता है जब आप पहले से ही उत्तर जानते हैं, इसलिए बोलना, सहायक नहीं है ...": यह उन उत्तरों के बारे में भी कहा जा सकता है जो केवल पुष्टि करते हैं कि पाठक पहले से ही जानता है या जानने के लिए सोचता है। यदि कोई उत्तर शब्दावली का परिचय देता है जो स्पष्ट नहीं है, तो डाउनवोटिंग से पहले अधिक विवरण मांगने के लिए टिप्पणियां लिखना संभव है।
जियोर्जियो

4
लूप पुनरावृत्ति का एक विशेष रूप नहीं हैं । कम्प्यूटेबिलिटी सिद्धांत और उदाहरण के लिए सैद्धांतिक WHILE भाषा और calcul-पथरी देखें। हां, कुछ भाषाएं लूप्स को सिंटैक्टिक शुगर के रूप में उपयोग करती हैं, वास्तव में पर्दे के पीछे पुनरावृत्ति का उपयोग करती हैं, लेकिन वे ऐसा कर सकते हैं क्योंकि पुनरावृत्ति और पुनरावृत्ति समान रूप से अभिव्यंजक हैं , इसलिए नहीं कि वे समान हैं।
पॉलिग्नोम जूल

-1

एक समय लूप पुनरावृत्ति से अलग है।

जब कोई फ़ंक्शन कहा जाता है, तो निम्न कार्य होता है:

  1. स्टैक में एक स्टैक फ्रेम जोड़ा जाता है।

  2. फ़ंक्शन की शुरुआत करने के लिए कोड पॉइंटर चलता है।

जब कुछ समय के लिए लूप अंत में होता है तो निम्न होता है:

  1. एक शर्त पूछती है कि क्या कुछ सच है।

  2. यदि हां, तो कोड एक बिंदु पर कूदता है।

सामान्य तौर पर, जबकि लूप निम्नलिखित छद्मकोश के समान होता है:

 if (x)

 {

      Jump_to(y);

 }

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


2
आप एक प्रक्रिया कॉल और थोड़ी देर के लूप के कार्यान्वयन के बारे में बात कर रहे हैं और, क्योंकि वे अलग तरीके से लागू किए जाते हैं, आप निष्कर्ष निकालते हैं कि वे अलग हैं। हालांकि, वैचारिक रूप से बहुत समान हैं।
जियोर्जियो

1
संकलक के आधार पर, एक अनुकूलित, इनलाइन रिकर्सन कॉल अच्छी तरह से समान लूप के रूप में एक ही विधानसभा का उत्पादन कर सकता है।
हाइड

@ लिड ... जो केवल प्रसिद्ध तथ्य के लिए एक उदाहरण है कि एक को दूसरे के माध्यम से व्यक्त किया जा सकता है; इसका मतलब यह नहीं है कि वे समान हैं। द्रव्यमान और ऊर्जा की तरह एक सा। बेशक कोई यह तर्क दे सकता है कि समान उत्पादन के सभी तरीके "समान" हैं। यदि दुनिया परिमित थी, तो सभी कार्यक्रम अंत में विवश होंगे।
पीटर -

@ जियोर्जियो नाह, यह एक तार्किक विवरण है, कार्यान्वयन नहीं। हम जानते हैं कि दो चीजें समान हैं ; लेकिन समानता की पहचान नहीं है , क्योंकि प्रश्न (और उत्तर) वास्तव में हम परिणाम के लिए कैसे प्राप्त करते हैं, अर्थात वे आवश्यक रूप से एल्गोरिथम विवरण (जो स्टैक और चर आदि के रूप में व्यक्त किए जा सकते हैं) शामिल हैं।
पीटर -

1
@ पीटरए.साइडर यस, लेकिन इस उत्तर में कहा गया है "सभी का सबसे महत्वपूर्ण ... अलग-अलग विधानसभा कोड", जो बिल्कुल सही नहीं है।
हाइड

-1

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

मुझे यकीन नहीं है कि यह विवादास्पद क्यों है। एक स्टैक के साथ पुनरावृत्ति और पुनरावृत्ति एक ही कम्प्यूटेशनल प्रक्रिया है। वे एक ही "घटना" हैं, इसलिए बोलने के लिए।

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

स्पष्ट करने के लिए, प्रश्न का उत्तर कुछ समय के लिए आंतरिक रूप से लूप है? एक निश्चित संख्या है , या कम से कम "तब तक नहीं जब तक कि आपके पास ढेर न हो"।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.