꼬리 재귀 (Tail Recursion)

일반적인 프로그래밍에서 재귀는 "메모리 소모자"로 여겨집니다. 하지만 "꼬리 먹는" 기술을 마스터하면 재귀가 중간 반복과 같이 효율적으로 동작할 수 있습니다.

1. 일반적인 재귀 vs. 꽈리 재귀

阶승 계산 (예: 5! = 5 × 4 × 3 × 2 × 1)을 통해 차이를 비교합니다.

일반적인 재귀: "堆疊"의 대표적인 예


def factorial(n):
    if n == 1:
        return 1
    return n * factorial(n - 1)  # 결과를 받기 위해 기다려야 한다
  • 과정: 컴퓨터는 "기다리며 결과를 받을 것"이라는 정보를 저장합니다. 각 레벨마다 다음 레벨의 결과를 기다리며 스택(StackSize)이 점점 쌓입니다.
  • 문제: 만약 n이 매우 크면 스택이 넘칠 수 있습니다 (Stack Overflow).

꼬리 재귀: 우아한 "꼬리 먹기"


def tail_factorial(n, accumulator=1):
    if n == 1:
        return accumulator
    return tail_factorial(n - 1, n * accumulator)  # 마지막 단계는 함수호출만 있고 연산은 없음
  • 과정: 모든 연산은 다음 레벨로 전달되기 전에 완료됩니다 (결과가 accumulator에 저장됨).
  • 원리: 만약 마지막 단계가 함수를 호출만 하면 연산이 필요없다면 컴퓨터는 현재의 메모리 공간을 바로覆사하여 반복적으로 사용할 수 있습니다.

2. 왜 "꼬리"라고 하는가?

컴파일러 수준에서 이 최적화는 TCO (Tail Call Optimization)라고 합니다.

  • 멈추고 서 있는 것: 일반적인 재귀는 왕복 달리기와 같습니다. 가는 길에 많은 것을 가지고, 돌아올 때 다시 처리합니다.
  • 衔尾蛇 (Ouroboros): 꽈리 재귀는 앞으로만 전진합니다. 현재의 "상태"를 바로 다음 순간의 자신에게 전달합니다. CPU에게 이것은 더 이상 중첩호출이 아니라 단순한 점프 (Jump) 명령입니다. 그것은 오래된 매개변수들을 지속적으로 소모합니다 (꼬리).

3. 이 기술의 한계

모든 언어가 이 기술을 지원하는 것은 아닙니다:

완벽하게 지원 상황에 따라 지원 명확하게 지원하지 않음
함수형 언어 (Erlang, Haskell, Elixir, Scheme) C/C++, Rust (옵션을 켜야함) Python, Java (기본적으로 호출 스택 정보를 보존하기 위해 이 기능을 지원하지 않음)

참고: Python에서 꽈리 재귀 형식을 사용한다고 해도 여전히 메모리를 소모합니다. Python 인터프리터는 이 "자동 회수" 메CHANISM을 구현하지 않습니다.

4. 왜 배우는가?

  1. 사고 방식 전환: 이는 "함수형 프로그래밍"의 문을 열고, 전역 변수 대신 상태 전달을 배우게 합니다.
  2. 성능 극대화: TCO를 지원하는 언어 (예: Scala 또는 특정 JS 엔진)에서 이는大规模 데이터를 처리하면서 셔UTDOWN하지 않는 유일한 방법입니다.

5. 재미있는 예제: 피보나契 수열

일반적인 재귀로 피보나契 수열의 100번째 값을 계산하면 컴퓨터가 멈춥니다. 하지만 꽈리 재귀 형식으로 작성하면 즉시 결과를 얻을 수 있습니다.

태그: 꼬리재귀 재귀호출최적화 함수형프로그래밍

6월 15일 22:47에 게시됨