루프를 사용할 수 있는 경우라면 거의 대부분 재귀도 쓸 수 있다. 재귀가 빛을 발하는 예제를 살펴보자.
아래 함수의 경우 무한대로 음수를 출력한다.
function countdown(number) {
console.log(number);
countdown(number - 1);
}
영원히 지속되는 것을 막기 위해 조건문을 추가한다.
function countdown(number) {
console.log(number);
if (number === 0) {
return;
} else {
countdown(number - 1);
}
}
이처럼 모든 재귀 함수에는 무한대로 호출되지 않게 하는 기저 조건이 적어도 하나 있어야 한다.
Factorial을 계산하는 예제를 살펴보자.
function factorial(number) {
if (number === 1) {
return 1;
} else {
return number * factorial(number - 1);
}
}
재귀 코드가 어떻게 동작하는지 살피는 순서는 다음과 같다.
컴퓨터는 스택을 사용해 어떤 함수를 호출 중인지 기록한다. 스택에는 가장 위 원소만 팝할 수 있다는 제약이 있다. 가장 위 원소는 가장 최근에 호출된 함수, 즉 컴퓨터가 다음으로 마무리해야 할 함수이니 이러한 제약은 재귀에 이상적이다.
이 방식을 ‘호출 스택을 통해 값 위로 전달하기'라고 부른다. 즉, 각 재귀 함수는 계산된 값을 보무 함수에 반환한다. 마침내 최초로 호출된 함수가 최종 값을 계산한다.
무한 재귀에서 컴퓨터는 반복해서 같은 함수를 호출 스택에 푸쉬한다. 단기 메모리에 더 이상 데이터를 저장할 공간이 없을 때까지 호출 스택은 점점 늘어난다. 결국 스택 오버플로우라는 오류가 발생한다.
주어진 디렉토리의 모든 하위 디렉토리를 출력해야한다고 생각해보자.
몇 단계나 깊이 들어가야 하는지 모르는 상황에서는 재귀가 자연스럽다.
array = [
1,
2,
3,
[4, 5, 6],
7,
[8,
[9, 10, 11,
[12, 13, 14]
]
],
[15, 16, 17, 18, 19,
[20, 21, 22,
23, 24, 25,
[26, 27, 28, 29]
], 30, 31
], 32
]
배열 내 모든 숫자를 출력하는 재귀함수를 작성하라.
function printAllItems(array) {
array.forEach(element => {
if (Array.isArray(element)) {
printAllItems(element);
} else {
console.log(element);
}
});
}