올바른 꼬리 호출과 재귀

리스펙스는 꼬리 적용을 명시적인 반복문 안에서 처리하며 현재 인터프리터 프레임을 교체합니다. 그래서 자기 꼬리 재귀와 상호 꼬리 재귀는 논리 연속을 늘리지 않습니다.

언제 필요한가요?

루프를 재귀로 적는 편이 자연스러울 때 꼬리 호출이 중요해집니다. 재귀 안내에서 부른 그 자리, 즉 호출의 결과가 곧바로 지금 식의 결과가 되는 자리가 꼬리 위치입니다. 재귀 호출이 그 자리에 있으면 리스펙스는 연속을 하나 더 쌓는 대신 지금 연속을 교체합니다.

실행으로 확인하기

LISPEX
(let loop ((n 10000) (acc 0)) (if (= n 0) acc (loop (- n 1) (+ acc 1))))

관측 결과

OUTPUT
10000

예제 읽기

이름 있는 letloop를 만듭니다. 각 분기는 acc를 돌려주거나, 더 작아진 n과 새 acc를 들고 곧바로 loop를 다시 부릅니다. 호출 뒤에 남아 있는 덧셈이 없습니다. 그래서 1만 번을 돌아도 논리적으로는 루프 하나로 끝납니다.

이 순서로 생각하세요

  • 꼬리 위치에는 마지막 본문과 if의 두 가지, 정규화된 cond·case, 마지막 and·or, do, call-with-values, apply가 들어갑니다.
  • 올바른 꼬리 동작이 말하는 것은 연속이 얼마나 자라느냐입니다. 실행 시간이 같다거나 무제한으로 실행된다는 뜻은 아닙니다.
  • 꼬리 재귀 프로그램에도 자원 한도는 그대로 적용됩니다. 이 한도는 결정적이고 프로필마다 다릅니다.

빠르게 고르기

형태꼬리 호출?이유
(if done? answer (loop next))선택한 분기 결과를 그대로 반환
(begin (display x) (loop next))호출이 마지막 폼
(+ x (loop next))아니요재귀 결과 뒤에 덧셈이 남음
(map loop items)콜백은 아니요map이 콜백 결과를 더 처리
꼬리 위치의 (apply loop args)apply가 호출자의 꼬리 위치를 보존

흔한 오해

비꼬리 재귀는 선언된 자원 한도에 도달할 수 있습니다.

현재 경계

  • 자원 오류가 깨끗하게 났다는 사실은 네이티브와 WebAssembly의 임계값이 같다는 증거가 되지 않습니다.

다음으로

기저 사례와 누산기 변환은 재귀 가이드에서 익히세요. 꼬리 안전이 곧 무제한을 뜻하지 않는 이유는 결정성과 자원에서 설명합니다.

재귀 가이드 · 결정성과 자원