दिलचस्प पोस्ट
फ़ायरफ़ॉक्स (वेबएक्सटेंशन) में "ब्राउज़र एक्शन" पॉपअप को खोलने के लिए एक वैश्विक हॉटकी कैसे तैयार करें? सी # विस्तार विधियों के बराबर जावा JQuery के साथ जावास्क्रिप्ट ऑब्जेक्ट के रूप में फॉर्म डेटा कन्वर्ट करें दोहराया टाइपिंग – सी में अवैध है लेकिन सी ++ में मान्य है? एक + = b और a = + b के बीच क्या अंतर है, यह भी एक ++ और ++ ए? स्ट्रिंग को जावास्क्रिप्ट में चर नाम में कनवर्ट करें किसी प्रोग्राम के रिटर्न वैल्यू को फ़ंक्शन के एडिबल परिवार के सदस्य को कॉल करने के लिए कैसे पहुंचा? dplyr: "n () में त्रुटि: फ़ंक्शन को सीधे नहीं बुलाया जाना चाहिए" स्टैकपैनल के लिए क्षैतिज स्क्रॉल काम नहीं करता अभिविन्यास परिवर्तन कैसे पता लगा सकता है? सम्मिलित पंक्ति की पहचान प्राप्त करने का सर्वोत्तम तरीका? JSON ऑब्जेक्ट भेज रहा है और पार्सिंग स्प्रिंग जावा कॉन्फ़िग बनाम जेबॉस 7 स्प्रिंग प्रोटोटाइप बीन को एक सिंगलटन में निकाल दिया गया सीएसएस के लिए प्राथमिकता का क्रम क्या है?

टेल टेलर अनुकूलन क्या है?

बहुत आसानी से, पूंछ कॉल अनुकूलन क्या है? अधिक विशेष रूप से, क्या कोई छोटे से कोड स्निपेट दिखा सकता है जहां इसे लागू किया जा सकता है, और क्यों नहीं, इसका स्पष्टीकरण के साथ क्यों?

Solutions Collecting From Web of "टेल टेलर अनुकूलन क्या है?"

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

योजना कुछ प्रोग्रामिंग भाषाओं में से एक है जो इस बात की गारंटी देती है कि किसी भी कार्यान्वयन को इस अनुकूलन को प्रदान करना चाहिए (जावास्क्रिप्ट भी, एक बार ES6 को अंतिम रूप दिया जायेगा) , इसलिए ये योजना में कारक समारोह के दो उदाहरण हैं:

(define (fact x) (if (= x 0) 1 (* x (fact (- x 1))))) (define (fact x) (define (fact-tail x accum) (if (= x 0) accum (fact-tail (- x 1) (* x accum)))) (fact-tail x 1)) 

पहला कार्य पुनरावर्ती पूंछ नहीं है क्योंकि जब रिकर्सिव कॉल किया जाता है, तो फ़ंक्शन को कॉल रिटर्न के बाद नतीजे के साथ गुणा करने के लिए ट्रैक की आवश्यकता होती है। जैसे, ढेर इस प्रकार दिखता है:

 (fact 3) (* 3 (fact 2)) (* 3 (* 2 (fact 1))) (* 3 (* 2 (* 1 (fact 0)))) (* 3 (* 2 (* 1 1))) (* 3 (* 2 1)) (* 3 2) 6 

इसके विपरीत, पूंछ पुनरावर्ती कारक के लिए स्टैक ट्रेस निम्नानुसार दिखता है:

 (fact 3) (fact-tail 3 1) (fact-tail 2 3) (fact-tail 1 6) (fact-tail 0 6) 6 

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

चलिए एक सरल उदाहरण के माध्यम से चलते हैं: सी में लागू होने वाली कारक समारोह।

हम स्पष्ट पुनरावर्ती परिभाषा के साथ शुरू करते हैं

 unsigned fac(unsigned n) { if (n < 2) return 1; return n * fac(n - 1); } 

फ़ंक्शन एक पूंछ कॉल के साथ समाप्त होता है यदि फ़ंक्शन रिटर्न से पहले अंतिम ऑपरेशन एक अन्य फ़ंक्शन कॉल होता है। यदि यह कॉल एक ही समारोह को आमंत्रित करती है, तो यह पूंछ-पुनरावर्ती है।

हालांकि fac() पहली नजर में पूंछ-पुनरावर्ती दिखता है, ऐसा नहीं है कि वास्तव में क्या होता है

 unsigned fac(unsigned n) { if (n < 2) return 1; unsigned acc = fac(n - 1); return n * acc; } 

अर्थात पिछले ऑपरेशन गुणा है और फ़ंक्शन कॉल नहीं है।

हालांकि, कॉल श्रृंखला को अतिरिक्त तर्क के रूप में संचित मूल्य से गुजरने के द्वारा fac() को फिर से लिखना संभव होता है और केवल अंतिम परिणाम को रिटर्न वैल्यू के रूप में बदलना संभव है:

 unsigned fac(unsigned n) { return fac_tailrec(1, n); } unsigned fac_tailrec(unsigned acc, unsigned n) { if (n < 2) return acc; return fac_tailrec(n * acc, n - 1); } 

अब, यह क्यों उपयोगी है? क्योंकि हम पूंछ कॉल के तुरंत बाद वापस आते हैं, हम पूंछ की स्थिति में समारोह को लागू करने से पहले पिछला स्टैकफ्रेम को त्याग सकते हैं, या, रिकर्सिव फ़ंक्शंस के मामले में, स्टैकफ्रेम का पुन: उपयोग करते हैं।

पूंछ कॉल अनुकूलन हमारे रिकर्सिव कोड को बदल देती है

 unsigned fac_tailrec(unsigned acc, unsigned n) { TOP: if (n < 2) return acc; acc = n * acc; n = n - 1; goto TOP; } 

इसे एफआईसी fac() में शामिल किया जा सकता है और हम यहां पहुंच सकते हैं

 unsigned fac(unsigned n) { unsigned acc = 1; TOP: if (n < 2) return acc; acc = n * acc; n = n - 1; goto TOP; } 

जो कि के बराबर है

 unsigned fac(unsigned n) { unsigned acc = 1; for (; n > 1; --n) acc *= n; return acc; } 

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

टीसीओ (टेल कॉल ऑप्टिमाइज़ेशन) एक ऐसी प्रक्रिया है जिसके द्वारा एक स्मार्ट कंपाइलर एक फ़ंक्शन को कॉल कर सकता है और कोई अतिरिक्त स्टैक स्पेस नहीं ले सकता है। एक ही स्थिति में ऐसा होता है, यदि फ़ंक्शन में अंतिम अनुदेश दिया जाता है तो फ़ंक्शन को कॉल करता है g (नोट: जी एफ हो सकता है )। यहां की कुंजी है कि अब एफ को स्टैक स्पेस की आवश्यकता नहीं है – यह केवल जी को कॉल करता है और फिर जो कुछ भी वापस लौटाता है वह वापस लौटाता है। इस मामले में ऑप्टिमाइज़ेशन किया जा सकता है कि जी सिर्फ रन करता है और जो कुछ भी मूल्य देता है वह उस चीज़ को होता है जिसे च कहा जाता है।

यह ऑप्टिमाइज़ेशन पुनरावर्ती कॉल को विस्फोट की बजाय, लगातार स्टैक स्पेस बना सकता है।

उदाहरण: यह तथ्यात्मक कार्य TCOptimizable नहीं है:

 def fact(n): if n == 0: return 1 return n * fact(n-1) 

यह फ़ंक्शन इसके रिटर्न कथन में किसी अन्य फ़ंक्शन को कॉल करने के अलावा कुछ भी करता है।

यह नीचे कार्य TCOptimizable है:

 def fact_h(n, acc): if n == 0: return acc return fact_h(n-1, acc*n) def fact(n): return fact_h(n, 1) 

इसका कारण यह है कि इनमें से किसी भी कार्य में होने वाली अंतिम चीज दूसरे फ़ंक्शन को कॉल करना है।

संभवत: पूंछ कॉल, पुनरावर्ती पूंछ कॉल और टेल कॉल ऑप्टिमाइज़ेशन के लिए मुझे सबसे अच्छा उच्च स्तरीय वर्णन मिला है ब्लॉग पोस्ट

"क्या बिल्ली है: एक पूंछ कॉल"

दान सुगल्स्की द्वारा पूंछ कॉल अनुकूलन पर वह लिखता है:

एक पल के लिए, इस सरल कार्य पर विचार करें:

 sub foo (int a) { a += 15; return bar(a); } 

तो, आप क्या कर सकते हैं, या आपकी भाषा कंपाइलर कर सकते हैं? खैर, यह क्या कर सकता है प्रपत्र return somefunc(); कोड को बदल देता है return somefunc(); निम्न स्तरीय अनुक्रम pop stack frame; goto somefunc(); pop stack frame; goto somefunc(); । हमारे उदाहरण में, इसका मतलब है कि इससे पहले कि हम bar कॉल करते हैं, foo खुद को साफ करता है और फिर, bar को एक bar रूप में कॉल करने के बजाय, हम bar की शुरुआत में एक निम्न-स्तरीय goto ऑपरेशन करते हैं। Foo ने खुद को स्टैक से पहले ही साफ़ कर दिया है, इसलिए जब bar शुरू होता है तो ऐसा लगता है कि जो भी foo कहलाता है वह वास्तव में bar को foo है, और bar जब उसका मूल्य लौटता है, तो वह इसे सीधे foo लिए लौटा देता है, फिर इसे अपने कॉलर पर लौटें

और पूंछ पर लौटने पर:

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

तो यह है कि:

 sub foo (int a, int b) { if (b == 1) { return a; } else { return foo(a*a + a, b - 1); } 

चुपचाप में बदल जाता है:

 sub foo (int a, int b) { label: if (b == 1) { return a; } else { a = a*a + a; b = b - 1; goto label; } 

मैं इस विवरण के बारे में क्या पसंद करता हूं, यह अनिवार्य भाषा पृष्ठभूमि (सी, सी ++, जावा) से आने वाले लोगों के लिए संक्षिप्त और आसान है।

सबसे पहले ध्यान दें कि सभी भाषाओं को इसका समर्थन नहीं करता है।

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

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

इधर देखो:

http://tratt.net/laurie/tech_articles/articles/tail_call_optimization

जैसा कि आप शायद जानते हैं, रिकर्सिव फ़ंक्शन कॉल एक स्टैक पर कहर बरपा सकता है; यह स्टैक स्पेस से बाहर निकलने के लिए आसान है टेल कॉल ऑप्टिमाइज़ेशन एक ऐसा तरीका है जिसके द्वारा आप लगातार पुनरावर्ती शैली एल्गोरिदम बना सकते हैं जो निरंतर स्टैक स्पेस का उपयोग करता है, इसलिए यह बढ़ता और बढ़ता नहीं है और आप स्टैक त्रुटियां प्राप्त करते हैं।

  1. हमें यह सुनिश्चित करना चाहिए कि फंक्शन में कोई गेटो स्टेटमेंट नहीं हैं। फ़िलिशन कॉल की देखभाल से कैली फ़ंक्शन में अंतिम चीज होती है।

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

  3. टीसीओ हमेशा के लिए चल रहे कार्य का कारण हो सकता है:

     void eternity() { eternity(); }