어제 학습한 고차 함수에 이어 오늘은 재귀...
재귀는 기본적인 개념은 이해하고 있지만 사용하는 것에 정말 익숙해지고 싶은 부분이다. 실로 정말 깔끔한 코딩을 할 수 있기 때문이다. 구조가 비슷하면서 주어진 문제가 더 작은 문제가 나누어질 수 있고 중첩된 루프가 많거나 중첩된 정도를 미리 알 수 없을 때 for, while 문으로 해결을 했지만 이렇게 진행을 한다면 하드코딩을 감당해야 한다. 쪼개서 문제를 해결하는 방법을 재귀라고 예시와 함께 강의에서 말씀해주셨는데 이해하기 쉬웠던 것 같다. 문제를 더 이상 더 작아지지 않을 때까지 만든 후에 자기 자신을 호출하기도 하는 재귀 호출을 사용해서 코딩을 하는 것이다. 예시를 보고 아 이렇게 하면 되겠다 싶었는데 막상 내가 사용하려고 하니 그게 마음처럼 잘 되지 않는 것 같다. HA에서도 재귀를 사용하는데 정말 많은 시간이 들었다. 자연스러워질 때까지 많은 연습이 필요한 것 같다.
재귀는 반복문으로 표현 할 수 있고 깔끔한 코딩을 가능하게 해 주지만 재귀는 무한 반복을 방지하기 위해 반드시 탈출 조건이 있어야 한다고 한다. 그렇지 않으면 무한 루프에 빠진다는...ㅠㅠ 재귀로 코딩을 하게 되면 깔끔한 코딩은 물론 가독성이 좋다고 볼 수 있지만 값이 리턴되기 전까지 호출마다 call stack을 새로 생성하기 때문에 메모리를 많이 사용하게 된다. 그리고 성능 자체가 반복문보다 늦다고 한다.
하지만 꼬리 재귀(?)라는 것을 이용하면 위와 같은 문제점을 해결 할 수 있다고 하는데 꼬리 재귀란 재귀 호출이 끝난 후 현재 함수에서 추가 연산을 요구하지 않도록 구현하는 재귀의 형태라고 한다. 아래를 보면 알 수 있다. 꼬리 재귀의 장점을 얻기 위해서는 개발자가 재귀 함수를 꼬리 재귀 방식으로 개발하여야 하며 컴파일러가 꼬리 재귀 최적화를 지원해야 한다는 전제조건이 있어야 한다고 한다. 컴파일러가 꼬리 재귀를 최적화할 수 있으면 최적화 과정에서 꼬리 재귀를 반복문으로 변경하는 방식인 것이다. 그럼 기존의 재귀의 단점인 메모리 성능의 문제가 해결되는 것이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
function recursion(n) {
if (n === 0) {
return 1;
}
return n * recursion(n-1);
}
// 일반적인 재귀
function recursion(n, acc) {
if (n === 0) {
return 1;
}
return recursion(n-1, acc*n);
}
// 꼬리 재귀
|
cs |
그리고 강의 내용에 있었던 하노이의 탑은 재귀를 이해하기에 최적화 된 문제라고 한다. 세 개의 기둥과 서로 다른 크기인 n개의 원반들이 있는데 예를 들어 이 원반들을 기둥 1에서 3으로 옮기는 것이 목표인데 제한 조건은 원반은 반드시 하나의 기둥에 꽂혀있어야 하고 자신보다 작은 원반 위에 그 원반은 올려놓을 수 없다는 것이 조건이다. 수포자였던 나로서는 이 것을 보는데 한참 걸린 것 같다. 일반적으로 원판이 n개 일 때, 2^n-1 이동으로 원판을 모두 옮길 수 있다고 한다.
재귀를 사용하여야 할 부분과 반복문으로 충분히 가능한 상황을 잘 판단하여야겠다는 생각을 했다.
'개념정리' 카테고리의 다른 글
9/1[TIL]화살표 함수, this 키워드, call/apply/bind 메소드 (0) | 2020.09.01 |
---|---|
8/31[TIL]이머시브 첫날 & Git workflow & node.js (0) | 2020.08.31 |
8/29[TIL]고차함수 복습.. (0) | 2020.08.30 |
8/20[TIL] 함수 메소드 & 재귀 함수 & Recursion (0) | 2020.08.20 |
8/19[TIL] 비동기 호출&타이머API&함수메소드 (0) | 2020.08.19 |