केविन ने स्पष्ट रूप से बताया कि यह विशेष कोड स्निपेट कैसे काम करता है (साथ में यह काफी समझ से बाहर क्यों है), लेकिन मैं सामान्य काम में कैसे trampolines के बारे में कुछ जानकारी जोड़ना चाहता था ।
टेल-कॉल ऑप्टिमाइज़ेशन (TCO) के बिना, प्रत्येक फ़ंक्शन कॉल वर्तमान निष्पादन स्टैक में एक स्टैक फ़्रेम जोड़ता है । मान लें कि हमारे पास संख्याओं की उलटी गिनती के लिए एक समारोह है:
function countdown(n) {
if (n === 0) {
console.log("Blastoff!");
} else {
console.log("Launch in " + n);
countdown(n - 1);
}
}
यदि हम कॉल करते हैं countdown(3)
, तो विश्लेषण करें कि कॉल स्टैक बिना TCO के कैसे दिखेगा।
> countdown(3);
// stack: countdown(3)
Launch in 3
// stack: countdown(3), countdown(2)
Launch in 2
// stack: countdown(3), countdown(2), countdown(1)
Launch in 1
// stack: countdown(3), countdown(2), countdown(1), countdown(0)
Blastoff!
// returns, stack: countdown(3), countdown(2), countdown(1)
// returns, stack: countdown(3), countdown(2)
// returns, stack: countdown(3)
// returns, stack is empty
TCO के साथ, प्रत्येक पुनरावर्ती कॉल करने के लिए countdown
में है पूंछ स्थिति इसलिए कोई स्टैक फ्रेम आवंटित किया जाता है (कुछ भी नहीं कॉल का परिणाम लौटने के अलावा अन्य करने के लिए छोड़ दिया है)। TCO के बिना, स्टैक थोड़ा और भी बड़ा हो जाता है n
।
इस प्रतिबंध को countdown
फ़ंक्शन के चारों ओर एक आवरण डालकर ट्रैम्पोलिनिंग इस प्रतिबंध के आसपास हो जाता है । फिर, countdown
पुनरावर्ती कॉल नहीं करता है और इसके बजाय तुरंत कॉल करने के लिए एक फ़ंक्शन देता है। यहाँ एक उदाहरण कार्यान्वयन है:
function trampoline(firstHop) {
nextHop = firstHop();
while (nextHop) {
nextHop = nextHop()
}
}
function countdown(n) {
trampoline(() => countdownHop(n));
}
function countdownHop(n) {
if (n === 0) {
console.log("Blastoff!");
} else {
console.log("Launch in " + n);
return () => countdownHop(n-1);
}
}
यह कैसे काम करता है, इसकी बेहतर समझ पाने के लिए, आइए कॉल स्टैक देखें:
> countdown(3);
// stack: countdown(3)
// stack: countdown(3), trampoline
// stack: countdown(3), trampoline, countdownHop(3)
Launch in 3
// return next hop from countdownHop(3)
// stack: countdown(3), trampoline
// trampoline sees hop returned another hop function, calls it
// stack: countdown(3), trampoline, countdownHop(2)
Launch in 2
// stack: countdown(3), trampoline
// stack: countdown(3), trampoline, countdownHop(1)
Launch in 1
// stack: countdown(3), trampoline
// stack: countdown(3), trampoline, countdownHop(0)
Blastoff!
// stack: countdown(3), trampoline
// stack: countdown(3)
// stack is empty
प्रत्येक पर कदम countdownHop
समारोह छोड़ दिया आगे क्या होता है, बजाय एक समारोह लौटने कॉल करने के लिए का वर्णन करता है यह क्या होगा के प्रत्यक्ष नियंत्रण की तरह आगे क्या होने। ट्रम्पोलिन फ़ंक्शन तब इसे लेता है और इसे कॉल करता है, फिर जो भी फ़ंक्शन देता है उसे कॉल करता है , और इसी तरह जब तक "अगला चरण" नहीं होता है। इसे ट्रम्पोलिनिंग कहा जाता है क्योंकि नियंत्रण का प्रवाह प्रत्येक पुनरावर्ती कॉल और ट्रम्पोलिन कार्यान्वयन के बीच "बाउंस" होता है, बजाय फ़ंक्शन सीधे आवर्ती के बजाय। पुनरावर्ती कॉल कौन करता है पर नियंत्रण का परित्याग करके , ट्रैम्पोलिन फ़ंक्शन सुनिश्चित कर सकता है कि स्टैक बहुत बड़ा नहीं मिलता है। साइड नोट: यह कार्यान्वयन trampoline
सादगी के लिए रिटर्निंग वैल्यू को छोड़ देता है।
यह जानना एक मुश्किल हो सकता है कि क्या यह एक अच्छा विचार है। एक नया क्लोजर आवंटित करने वाले प्रत्येक चरण के कारण प्रदर्शन को नुकसान हो सकता है। चतुर अनुकूलन इस व्यवहार्य बना सकते हैं, लेकिन आप कभी नहीं जानते। ट्रम्पोलिनिंग ज्यादातर कठिन पुनरावृत्ति सीमाओं के आसपास पाने के लिए उपयोगी है, उदाहरण के लिए जब एक भाषा कार्यान्वयन अधिकतम कॉल स्टैक आकार सेट करता है।
loopy
अतिप्रवाह नहीं है क्योंकि यह स्वयं को नहीं बुलाता है ।