이번 시간의 목표는 이 방법을 학습하고 연습하면서 다양하게 생각하는 능력을 기르는 것입니다.
새로운 문제 해결 방법을 배우기 전에, 아래의 간단한 코딩 문제를 해결해 봅시다.
문제. 자연수의 리스트를 입력으로 받아 리스트의 합을 리턴하는 함수 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]
의 합을 구한다고 가정합시다.
[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은 (인자가 빈 배열이 아닌 경우) 실행과정 중에 자기 자신을 호출하기도 합니다. 이러한 호출 방식을 재귀 호출이라고 합니다.