कोई फंक्शन टेल-रिकर्सिव तब कहलाता है जब वह जो अंतिम काम करता है वह खुद को कॉल करना हो।
जब भी कोई फंक्शन कॉल किया जाता है, तो उसके लोकल वेरिएबलो और आर्गुमेंट के साथ एक स्टैक फ्रेम फंक्शन कॉल स्टैक के ऊपर रख दिया जाता है। जब फंक्शन अपना काम पूरा कर लेता है, तो वह स्टैक फ्रेम स्टैक से हटा दिया जाता है।
टेल-रिकर्सिव फंक्शन टेल कॉल ऑप्टिमाइज़ेशन (या टेल कॉल एलिमिनेशन) की सुविधा देते हैं। यह एक ऐसा ऑप्टिमाइज़ेशन है जिसमें अगला फंक्शन कॉल पिछले स्टैक फ्रेम का फिर से इस्तेमाल कर लेता है, अगर यह तय हो कि पिछले फंक्शन को उसकी अब ज़रूरत नहीं पड़ेगी। इससे फंक्शन कॉल स्टैक के ओवरफ्लो होने की चिंता कम हो जाती है। ओवरफ्लो तब होता है जब फंक्शन कॉल स्टैक पर इतने सारे फ्रेम हो जाएँ कि एक और फ्रेम बनाने के लिए मेमोरी ही न बचे।
कुछ शर्तों के तहत Elm कंपाइलर JavaScript में कंपाइल करते समय अपने आप टेल कॉल ऑप्टिमाइज़ेशन कर लेता है।
यह ऑप्टिमाइज़ेशन किसी रिकर्सिव फंक्शन में तब हो सकता है जब किसी ब्रांच में अंतिम ऑपरेशन खुद उसी फंक्शन को एक साधारण फंक्शन एप्लीकेशन में कॉल करना हो। चलिए कुछ उदाहरण देखते हैं:
factorial : Int -> Int
factorial n =
if n <= 1 then
n
else
n * factorial (n-1)
ऊपर दिया गया कोड टेल-रिकर्सिव नहीं है, क्योंकि else ब्रांच में सबसे अंतिम ऑपरेशन एक गुणा, n *, है।
factorial : Int -> Int
factorial n =
factorialHelper n n
factorialHelper : Int -> Int -> Int
factorialHelper n resultSoFar =
if n <= 1 then
resultSoFar
else
factorialHelper (n-1) (n * resultSoFar)
ऊपर दिया गया कोड टेल-रिकर्सिव है और ऑप्टिमाइज़ हो जाएगा, क्योंकि else ब्रांच में सबसे अंतिम ऑपरेशन factorialHelper का खुद को कॉल करना है। Int -> Int टाइप सिग्नेचर वाले फंक्शन के लिए यह संभव नहीं होगा। इसीलिए व्यवहार में टेल कॉल ऑप्टिमाइज़ेशन अक्सर हेल्पर फंक्शन बनाकर हासिल किया जाता है।
पाइपर को पाई बनाने का बहुत शौक है।
किसी को नहीं पता कि उसने पाई बनाना अपने नाम की वजह से चुना, या पाई बनाने के अपने शौक से मेल खाने के लिए उसने अपना नाम बदल लिया। पहली नज़र में दूसरी बात बहुत संभव नहीं लगती, लेकिन मामला यह है कि पाइपर को पाई बहुत पसंद हैं। वह हमेशा रसोई में कुछ न कुछ बदलती रहती है, अपनी रेसिपी में फेरबदल करती रहती है और अपना हुनर सुधारती रहती है। उसके दोस्तों को इससे बहुत खुशी होती है।
अब उसकी नई रुचि क्या है? जितनी संभव हो उतनी गोल पाई बनाना, गणितीय पूर्णता की हद तक, अपनी पसंदीदा संख्या की मदद से, और वह संख्या है: π।
पाइपर को बार-बार चरण दोहराकर π की गणना करने का एक बहुत प्यारा सूत्र मिला, न्यूटन/यूलर कन्वर्जेंस ट्रांसफॉर्मेशन:
π / 2 = sum for k from 0 to infinity of ( k! ) / ( 2 * k + 1 )!!
π की गणना करके पाइपर को उसकी गणितीय रूप से परिपूर्ण पाई बनाने में मदद कीजिए।
चलिए पहले थोड़ी तैयारी कर लें।
फैक्टोरियल ऑपरेटर, जिसे आमतौर पर ! लिखा जाता है, इस तरह परिभाषित किया जाता है:
0! = 1
n! = 1 * 2 * 3 * ... * n
factorial फंक्शन बनाइए, जो फैक्टोरियल की गणना टेल रिकर्सिव तरीके से करेगा।
factorial 4
-- 24
डबल फैक्टोरियल ऑपरेटर, जिसे आमतौर पर !! लिखा जाता है, इस तरह परिभाषित किया जाता है:
0!! = 1
n!! = 1 * 3 * 5 * ... * n (for odd n)
n!! = 2 * 4 * 6 * ... * n (for even n)
doubleFactorial फंक्शन बनाइए, जो फैक्टोरियल की गणना टेल रिकर्सिव तरीके से करेगा।
factorial 5
-- 15
factorial 6
-- 48
pipersPi फंक्शन बनाइए, जो न्यूटन/यूलर कन्वर्जेंस ट्रांसफॉर्मेशन सूत्र से तय संख्या में टर्म लेकर टेल रिकर्सिव तरीके से π का अनुमान लगाएगा।
π / 2 = sum for k from 0 to infinity of ( k! ) / ( 2 * k + 1 )!!
चलिए पहला टर्म मिलकर निकालते हैं।
अनंत की जगह 0 की ऊपरी सीमा लेने पर हमें यह मिलता है:
π / 2 ≈ Sum for k from 0 to 0 of ( k! ) / ( 2 * k + 1 )!!
π / 2 ≈ ( 0! ) / ( 2 * 0 + 1 )!!
π / 2 ≈ 0! / 0!!
π / 2 ≈ 1 / 1
π / 2 ≈ 1
π ≈ 2
हर अतिरिक्त टर्म अनुमान को और बेहतर बनाएगा।
pipersPi 0
-- 2.0
pipersPi 1
-- 2.6666666
Exercism पर साइन अप कीजिए और Elm को 28 कॉन्सेप्ट110 अभ्यास तथा असली इंसानों से मिलने वाली मेंटरिंग के साथ सीखिए और उसमें महारत हासिल कीजिए, वह भी बिल्कुल मुफ्त।