Kevin 은이 특정 코드 스 니펫이 어떻게 작동하는지 (간단히 이해할 수없는 이유와 함께) 간결하게 지적하지만 일반 작업 에서 트램폴린 이 어떻게 작동 하는지에 대한 정보를 추가하고 싶었습니다 .
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
집니다.
Trampolining은 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
단순성을 위해 값을 반환하지 않습니다.
이것이 좋은 아이디어인지 아는 것은 까다로울 수 있습니다. 각 단계에서 새 클로저를 할당하면 성능이 저하 될 수 있습니다. 영리한 최적화를 통해이 기능을 실현할 수는 있지만 알 수는 없습니다. Trampolining은 언어 구현이 최대 호출 스택 크기를 설정하는 경우와 같이 어려운 재귀 한계를 극복하는 데 주로 유용합니다.
loopy
때문에 오버플로 하지 않습니다 .