๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

๋นˆ ๊ตฌ๋ฉ ์ฑ„์šฐ๊ธฐ

[Java] Tail Recursion Optimization ๋Œ€์ฒด ๋ฐฉ๋ฒ•

์ถœ์ฒ˜

ChatGPT


์ž๋ฐ”์—์„œ๋Š” Tail Recursion Optimization(TCO)์ด ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€์›๋˜์ง€ ์•Š๋Š”๋‹ค. ์ด๋Š” ์ž๋ฐ”์˜ ์ปดํŒŒ์ผ๋Ÿฌ(Javac)์™€ JVM์˜ ์„ค๊ณ„๊ฐ€ TCO๋ฅผ ์ตœ์ ํ™”ํ•˜์ง€ ์•Š๋„๋ก ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. Tail Recursion Optimization์ด๋ž€ ์žฌ๊ท€ ํ•จ์ˆ˜์˜ ๋งˆ์ง€๋ง‰ ํ˜ธ์ถœ์„ ์Šคํƒ ํ”„๋ ˆ์ž„์„ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๊ณ  ์ฒ˜๋ฆฌํ•ด StackOverflowError๋ฅผ ๋ฐฉ์ง€ํ•˜๋Š” ์ตœ์ ํ™” ๊ธฐ๋ฒ•์ด๋‹ค.

 

์ž๋ฐ”์—์„œ Tail Recursion Optimization์„ ๋ฏธ์ง€์›ํ•˜๋Š” ์ด์œ 

1. JVM ์Šคํƒ ๋ชจ๋ธ

์ž๋ฐ”์˜ JVM์€ ์Šคํƒ ๊ธฐ๋ฐ˜์˜ ๊ฐ€์ƒ ๋จธ์‹ ์œผ๋กœ, ๊ฐ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ์‹œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์Šคํƒ ํ”„๋ ˆ์ž„์„ ์ถ”๊ฐ€ํ•œ๋‹ค. JVM์˜ ๊ธฐ๋ณธ ์„ค๊ณ„ ์›์น™ ์ค‘ ํ•˜๋‚˜๋Š” ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ๊ณผ ๊ด€๋ จ๋˜ ๋””๋ฒ„๊น… ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์ด๋ฏ€๋กœ, ๋ชจ๋“  ํ˜ธ์ถœ์ด ๋ช…์‹œ์ ์œผ๋กœ ์Šคํƒ ํ”„๋ ˆ์ž„์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜์–ด ์žˆ๋‹ค.

2. ์ตœ์ ํ™”์˜ ๋ถˆํ™•์‹ค์„ฑ

TCO๋Š” ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ํŒ๋‹จํ•ด ์ตœ์ ํ™”๋ฅผ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•˜๋Š”๋ฐ, ์ž๋ฐ” ์ปดํŒŒ์ผ๋Ÿฌ(Javac)๋Š” ์ด๋Ÿฐ ์ตœ์ ํ™”๋ฅผ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ž๋ฐ”๋Š” ์„ฑ๋Šฅ๋ณด๋‹ค ์•ˆ์ •์„ฑ๊ณผ ๋””๋ฒ„๊น… ๊ฐ€๋Šฅ์„ฑ์„ ๋” ์ค‘์š”ํ•˜๊ฒŒ ์—ฌ๊ธฐ๊ธฐ ๋•Œ๋ฌธ์— JVM ๋ ˆ๋ฒจ์—์„œ TCO๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค.

3. ๋””๋ฒ„๊น… ๋ฐ ์Šคํƒ ์ถ”์ 

์ž๋ฐ”๋Š” ๋””๋ฒ„๊น…๊ณผ ์Šคํƒ ์ถ”์ (Stack Trace)์„ ์‰ฝ๊ฒŒ ํ•˜๋„๋ก ์„ค๊ณ„๋˜์—ˆ๋‹ค. ์žฌ๊ท€ ํ˜ธ์ถœ์—์„œ ์Šคํƒ ํ”„๋ ˆ์ž„์„ ์ œ๊ฑฐํ•˜๋ฉด ๋””๋ฒ„๊น… ์‹œ์— ํ˜ธ์ถœ ์Šคํƒ์ด ์†์‹ค๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ๊ฐ€ ๋” ์–ด๋ ค์›Œ์ง„๋‹ค.

 

์ž๋ฐ”์—์„œ Tail Recursion Optimization์„ ๋Œ€์ฒดํ•˜๋Š” ๋ฐฉ๋ฒ•

์ž๋ฐ”์—์„œ TCO๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•  ์ˆ˜๋Š” ์—†์ง€๋งŒ, ๋น„์Šทํ•œ ๊ฒฐ๊ณผ๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

1. ๋ฐ˜๋ณต๋ฌธ(Iterative) ์‚ฌ์šฉ

์žฌ๊ท€๋ฅผ ๋ฐ˜๋ณต๋ฌธ์œผ๋กœ ๋ณ€ํ™˜ํ•ด ์Šคํƒ ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฐ˜๋ณต๋ฌธ์€ ์Šคํƒ ํ”„๋ ˆ์ž„์„ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๊ณ  ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ ˆ์•ฝํ•˜๋ฏ€๋กœ, ์ผ๋ฐ˜์ ์œผ๋กœ ๊นŠ์€ ์žฌ๊ท€๋ณด๋‹ค ๋” ์•ˆ์ „ํ•˜๋‹ค.

public int factorial(int n) {
    int result = 1;
    for (int i = 2; i <= n; i++) {
        result *= i;
    }
    return result;
}

 

์ด ์ฝ”๋“œ๋Š” ์žฌ๊ท€ ๋Œ€์‹  ๋ฐ˜๋ณต๋ฌธ์„ ์‚ฌ์šฉํ•ด ํŒฉํ† ๋ฆฌ์–ผ์„ ๊ณ„์‚ฐํ•œ๋‹ค. ์Šคํƒ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์žฌ๊ท€ ๊นŠ์ด์— ์ œํ•œ์ด ์—†๋‹ค.

 

2. ๋ช…์‹œ์  ์Šคํƒ ์‚ฌ์šฉ

ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ์ง์ ‘ ์Šคํƒ ์ž๋ฃŒ ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์žฌ๊ท€๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๋ฐฉ๋ฒ•์€ ์žฌ๊ท€๋ฅผ ๋ฐ˜๋ณต๋ฌธ์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋ฉด์„œ ์žฌ๊ท€ ํ˜ธ์ถœ์˜ ํŠน์„ฑ์„ ์œ ์ง€ํ•œ๋‹ค.

public int factorial(int n) {
    Stack<Integer> stack = new Stack<>();
    int result = 1;
    
    while (n > 1) {
        stack.push(n);
        n--;
    }
    
    while (!stack.isEmpty()) {
        result *= stack.pop();
    }
    
    return result;
}

 

์œ„์ธผ ์ฝ”๋“œ์—์„œ๋Š” ์ž๋ฐ”์˜ Stack ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ด ํŒฉํ† ๋ฆฌ์–ผ ๊ณ„์‚ฐ์„ ์œ„ํ•œ ๋ช…์‹œ์  ์Šคํƒ์„ ๊ด€๋ฆฌํ•œ๋‹ค.

 

3. ํŠธ๋ŸผํŽ„๋ฆฐ (Trampoline) ํŒจํ„ด

ํŠธ๋žจํŽ„๋ฆฐ ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์—ฌ ์žฌ๊ท€ ํ˜ธ์ถœ์„ ๋ฐ˜๋ณต๋ฌธ์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค. ์ด ํŒจํ„ด์€ ๊ผฌ๋ฆฌ ์žฌ๊ท€๋ฅผ ๋ฐ˜๋ณต์ ์œผ๋กœ ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ , ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ ๊ฐ’์„ ํ™œ์šฉํ•˜์—ฌ ๋‹ค์Œ ํ˜ธ์ถœ์„ ๊ฒฐ์ •ํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค.

import java.util.function.Supplier;

public class Trampoline<T> {
    private final Supplier<Trampoline<T>> nextStep;

    private Trampoline(Supplier<Trampoline<T>> nextStep) {
        this.nextStep = nextStep;
    }

    public T result() {
        Trampoline<T> step = this;
        while (step.nextStep != null) {
            step = step.nextStep.get();
        }
        return step.result();
    }

    public static <T> Trampoline<T> more(Supplier<Trampoline<T>> nextStep) {
        return new Trampoline<>(nextStep);
    }

    public static <T> Trampoline<T> done(T result) {
        return new Trampoline<>(null);
    }
}

// Factorial using Trampoline
public static Trampoline<Integer> factorial(int n, int result) {
    if (n == 1) {
        return Trampoline.done(result);
    }
    return Trampoline.more(() -> factorial(n - 1, n * result));
}

public static void main(String[] args) {
    int n = 5;
    int result = factorial(n, 1).result();
    System.out.println("Factorial of " + n + " is: " + result);
}

 

์œ„์˜ ์ฝ”๋“œ์—์„œ๋Š” Trampoline ํŒจํ„ด์„ ์‚ฌ์šฉํ•ด ํŒฉํ† ๋ฆฌ์–ผ ๊ณ„์‚ฐ์„ ์žฌ๊ท€์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•˜์ง€๋งŒ ์Šคํƒ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

 

๊ฒฐ๋ก 

์ž๋ฐ”์—์„œ๋Š” Tail Recursion Optimization์„ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€์›ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ, ์žฌ๊ท€๊ฐ€ ๊นŠ์–ด์งˆ ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ ๋ฐ˜๋ณต๋ฌธ์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ฑฐ๋‚˜, ๋ช…์‹œ์  ์Šคํƒ ์‚ฌ์šฉ, ํŠธ๋žจํŽ„๋ฆฐ ํŒจํ„ด๊ณผ ๊ฐ™์€ ๋‹ค๋ฅธ ๋Œ€์•ˆ์ ์ธ ๋ฐฉ๋ฒ•๋“ค์„ ์‚ฌ์šฉํ– ์Šคํƒ ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ธฐ๋ฒ•๋“ค์€ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ์„ ์ตœ์ ํ™”ํ•˜๊ณ , ๊นŠ์€ ์žฌ๊ท€ ํ˜ธ์ถœ์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ๋“ค์„ ํ”ผํ•  ์ˆ˜ ์žˆ๋„๋ก ๋•๋Š”๋‹ค.