이번 시간의 목표는 이 방법을 학습하고 연습하면서 다양하게 생각하는 능력을 기르는 것입니다.
새로운 문제 해결 방법을 배우기 전에, 아래의 간단한 코딩 문제를 해결해 봅시다.
문제. 자연수의 리스트를 입력으로 받아 리스트의 합을 리턴하는 함수 `arrSum을` 작성하세요.
(자연수) 리스트의 합을 구하는 알고리즘
sum
을 0
으로 초기화한다.sum
에 더한다.코드
function arrSum(arr) {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
이제 이 문제를 다른 각도에서 생각해 보겠습니다.자연수의 리스트 [10, 3, 6, 2]
의 합을 구한다고 가정합시다.
[10, 3, 6, 2]
의 합을 구하는 건 신경쓰지 않고, 대신에 [3, 6, 2]
의 합을 구하는 방법을 생각해 봅니다.
[10, 3, 6, 2]
보다는 [3, 6, 2]
가 더 작으니 왠지 더 쉽게 풀릴 것 같기도 합니다. [3, 6, 2]
의 합을 구하는 방법을 알아낸다면, 이 값에 10
을 더하면 원래의 목적인[10, 3, 6, 2]
의 합을 구할 수 있습니다.[3, 6, 2]
의 합을 구하는 것도 조금 버겁게 느껴져서, 대신에 [6, 2]
의 합을 구하는 방법을 생각해 봅니다.
[3, 6, 2]
보다는 [6, 2]
가 더 작아서 쉽게 풀 수 있을 것 같습니다. [6, 2]
의 합을 구하는 방법을 알아낸다면, 이 값에 3
을 더하면 [3, 6, 2]
의 합을 구할 수 있습니다.arrSum([3, 6, 2]) = 3 + arrSum([6, 2])
[6, 2]
대신 [2]
의 합을 구하는 방법을 생각해 봅니다. [2]
의 합에 6
을 더해 [6, 2]
의 합을 구할 수 있습니다.[2]
의 합을 구하는 건 매우 간단합니다. 2
입니다.이 과정을 아래의 간단한 코드로 표현할 수 있습니다.
arrSum([10, 3, 6, 2]) = 10 + arrSum([3, 6, 2]);
arrSum([3, 6, 2]) = 3 + arrSum([6, 2]);
arrSum([6, 2]) = 6 + arrSum([2]);
arrSum([2]) = 2 + arrSum([]);
arrSum([]) = 0;
이처럼 어떤 문제를 해결할 때, 구조는 동일하지만 더 작은 경우를 해결함으로써 그 문제를 해결하는 방법을 **재귀(recursion)**라고 합니다. 재귀의 과정을 arrSum
에 적용해보면 아래와 같습니다.
arrSum([10, 3, 6, 2]) = 10 + arrSum([3, 6, 2]);
arrSum([3, 6, 2]) = 3 + arrSum([6, 2]);
arrSum([6, 2]) = 6 + arrSum([2]);
arrSum([2]) = 2 + arrSum([]);
arrSum([]) = 0; // <-- 문제가 더는 작아지지 않는 순간
// 가장 작은 경우의 해결책을 적용한다.
arrSum([2]) = 2 + arrSum([]) = 2;
arrSum([6, 2]) = 6 + arrSum([2]) = 6 + 2 = 8;
arrSum([3, 6, 2]) = 3 + arrSum([6, 2]) = 3 + 8 = 11;
arrSum([10, 3, 6, 2]) = 10 + arrSum([3, 6, 2]) = 10 + 11 = 21;
arrSum을 아래와 같이 보다 엄밀하게(formally) 정의할 수 있습니다.
arrSum(arr)
1. arr이 빈 배열인 경우 = 0
2. 그 외의 경우 = arr[0] + arrSum(arr2)
(arr2는 arr의 첫 요소를 제외한 나머지 배열)
만약에 arrSum 함수를 javascript 코드를 구현할 경우 (코플릿 과제 중 하나입니다), arrSum은 (인자가 빈 배열이 아닌 경우) 실행과정 중에 자기 자신을 호출하기도 합니다. 이러한 호출 방식을 재귀 호출이라고 합니다.