2010-10-21 17 views
16

एक पुनरावर्तन का उपयोग कर कई कार्यात्मक भाषाओं में एक अच्छा अभ्यास माना जाता है। मुझे लगता है कि संकलक भाषात्मक कोड को अनुकूलित करने के तरीके के कारण यह अच्छा है।सी # में यह एल्गोरिदम में पुनरावर्ती कार्यों का उपयोग करने के लिए एक अच्छा अभ्यास है?

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

यदि आप कार्यात्मक भाषाओं और सी # में रिकर्सन का उपयोग करते हुए एल्गोरिदम के बीच कुछ तुलना (गति, स्मृति, पठनीयता) प्रदान करेंगे तो मैं सराहना करता हूं।

+5

कम से कम, जब यह समझ में आता है तो रिकर्सन का उपयोग करें (और आप कर सकते हैं, यानी जब यह स्टैक ओवरफ़्लो न हो) - उदा। पेड़ ट्रैवर्सल और एल्गोरिदम के लिए जो पुनरावृत्ति में परिवर्तित होने पर बहुत बदसूरत/जटिल हो जाते हैं। – delnan

+0

http://stackoverflow.com/questions/491376/why-doesnt-net-c-eliminate-tail-recursion –

उत्तर

16

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

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

चेक बाहर Creating Thread with Custom Stack Size,

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

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

+3

-1। सी # में रिकर्सन के साथ समस्या यह है कि आप जो भी ढेर आकार तय करते हैं, आप अभी भी इसके आगे की देखभाल कर सकते हैं। रिकर्सन (जिसे हम एक ऐसे फ़ंक्शन के रूप में समझते हैं जो प्रत्यक्ष या परोक्ष रूप से स्वयं को कॉल करता है) सामान्य मामले में असंबद्ध है: आप समय से पहले नहीं जानते कि कितना गहराई हो रही है। – harms

+0

@harms, यह स्पष्ट है और हर कोई इसे जानता है, जहां स्टैक ओवरफ्लो होता है, तो यह आपके गैर रिकर्सिव एल्गोरिदम में भी हो सकता है क्योंकि प्रत्येक कॉल को स्टैक, कतार या सूची पर एल्गोरिदम की कुछ प्रकार की धक्का देने की स्थिति की आवश्यकता होगी और जो बढ़ सकता है भी। –

+4

+1 यह इंगित करने के लिए कि एल्गोरिदम राज्य को * कहीं * जाना है। –

5

लूप हमेशा रिकर्सन से बाहर निकलता है क्योंकि स्टैक हमेशा आपके राज्य की तुलना में अधिक ओवरहेड होता है। बहुत सारे थ्रेडिंग ऑपरेशन भारी स्टैक पर चलते हैं ताकि आपको और गिरावट मिल सके।

हालांकि, पठनीयता एक बड़ा प्लस है इसलिए मैं व्यक्तिगत रूप से रिकर्सन का उपयोग तब तक करूँगा जब तक कि मुझे छवि प्रसंस्करण संचालन में प्रतिरूपता की हर बूंद की आवश्यकता न हो या मुझे उम्मीद है कि मेरे ढेर बहुत बड़े हो जाएं - हालांकि बग के कारण लगभग पूरी तरह से ढेर का प्रवाह होता है।

+0

इसलिए यदि आपके एल्गोरिदम में कोई अनंत लूप नहीं है तो आप स्टैक मेमोरी से बाहर नहीं होंगे, भले ही वह एक रिकर्सिव कॉल बनाते समय सी # फ़ंक्शन के स्टैक फ्रेम का पुन: उपयोग नहीं करता है? – Vitalij

+1

नहीं ... और हाँ। यदि भाषा पूंछ-कॉल रिकर्सन का समर्थन करती है, तो पहले कॉल से परे कोई स्टैक नहीं है (जो पुनरावृत्त संस्करण में भी मौजूद है)। सी # ऐसी भाषा नहीं है इसलिए पुनरावृत्ति संस्करण अधिक स्मृति/गति-कुशल होगा। –

+0

असल में, जबकि सी # स्पष्ट रूप से पूंछ रिकर्सन का समर्थन नहीं करता है, वहीं जब ऑप्टिमाइज़र रिकर्सिव कॉल के बाद कोई और काम नहीं करता है तो मानक रिकर्सन के स्थान पर इसका उपयोग करने का प्रयास करता है। मैं इस प्रश्न में एक बग को पुन: उत्पन्न करने की कोशिश कर रहा था: http://stackoverflow.com/questions/3915636/linqpad-just-crashed-on-me-is-my-code-anywhere-on-the-disk पर निर्भर इस व्यवहार की सलाह नहीं दी जाएगी। बिंदु पूरी तरह से याद करने के लिए – spender

2

यहां यह पोस्ट Recursive iterator performance रिकर्सिव संस्करण बनाम गैर-पुनरावर्ती ऑपरेशन के बीच अंतर को विस्तार से समझाता है। परिणाम दिलचस्प हैं। की जाँच करें

4

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

+1

लगभग लेकिन सही नहीं है। @ अलीस्टाड के जवाब पर मेरी टिप्पणी देखें। – spender

+0

टेल कॉल ऑप्टिमाइज़ेशन केवल तभी किया जाता है जब आप स्पष्ट रूप से 32-बिट CPUs के लिए संकलित नहीं करते हैं। http://goo.gl/AofK –

1

जब आपको रिकर्सन की आवश्यकता होती है, तो आपको गहराई से पहले पेड़ चलने, या रिकर्सिव-वंश पार्सिंग की आवश्यकता होती है।

यदि आपके पास कोई विकल्प है, जैसे लूप के बजाय रिकर्सन का उपयोग करना, तो हो सकता है कि यह उस भाषा का एक कार्य है जिसमें आप हैं। इसे सरल रखें।

1

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

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

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

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