➿ 프로그래밍에서 재귀의 사전적 정의
"어떠한 것을 정의할 때 자기 자신을 참조하는 것"
위키백과
for문
으로 완전히 동일히 작동하는 코드로직을 구성할 수 있지만, 재귀를 돋보이게 만드는 주요 요인 중 하나는 재귀를 사용하지 않을 때와 달리 몇 줄의 코드로 복잡한 작업을 수행할 수 있다는 것이다.push
되어, 순차적으로 실행된다따라서, 콜 스택이 재귀함수와도 밀접한 연관이 있는데,
1. 콜 스택은 프로그램 실행 중 발생하는 함수 호출을 추척한다
2. 재귀함수는 자기자신을 다시 호출하므로, 각 재귀 호출마다 콜 스택에 호출정보가 추가된다(push)
3. 재귀함수내의 걸어놓은 조건(breakPoint)에 달하면, 해당 호출이 콜스택에서 제거(pop)되고, 각 함수의 반환값에 따라 최종 결과가 결정되는 흐름이다.
해당 재귀함수로 이루어진 콜스택에서 가장 상단의 함수가 종료조건을 만나면?
첫번째인수 x
에 두번째인수n
승 해주는 pow()
를 만들어보자
일반적인 for문에서
function pow1(x, n) {
let result = 1;
//n만큼 반복문을 돌면서 result에 x를 곱한다
for (let i = 0; i < n; i++) {
result *= x;
}
return result;
}
pow1(2,3) //2의 3승인 8이 나오게된다.
pow1()
내의 result
라는 변수를 할당해 해당 변수에 인자로받은 n
만큼 돌면서, result
에 x
의 값을 곱한후 해당 resul
의 값을 반환하는 형태이다.이걸 재귀함수로 만들면?
const pow2 = (x, n) => {
if (n === 1) {
return x;
}
return x * pow2(x, n - 1);
};
pow2(2, 3);
n
의 값이 1
이됬을때 x
의 값을 반환한다는게 있는데, 이게 해당 재귀함수에서의 brakepoint 즉, 조건처리이다.x
에 pow2(x,n-1)
을 한 값을 곱하고 있다 이게 무슨의미냐면x
를 n-1
번 곱한 값을 반환한다는 의미이다.즉, pow2(x,n)
을 구하는것은 x
에다가 pow2(x,n-1)
을 한번 더 곱하는것과 같다.
pow2(2, 3)
은 2
에다가 pow2(2, 2)
를 곱한 값pow(2, 2)
는 2
에다가 pow(2, 1)
을 곱한 값 pow2(2,6)
으로 n의 인수로 6을 전달했을때를 디버깅툴로 확인해보자.
pow2()의 가장 첫번째 재귀함수 실행 (콜스택 첫번째로 쌓임)
pow2()의 두번째 재귀함수 실행
pow2()의 세번째 재귀함수 실행
pow2()의 네번째 재귀함수 실행
pow2()의 다섯번째 재귀함수 실행
pow2()의 여섯번째 재귀함수 실행 (n값이 1이 됨과 동시에 x를 반환하고 재귀함수가 종료되었다)
pow2()함수의 총 호출스택 여기선 인수가 총 6개이므로, 6개가 된 순간 위에서부터 아래로 제거된다
😒 for문이나 재귀함수나 코드양은 거기서거기인데??
이렇게 중첩으로된 객체가 존재할때,
const SocialPartiners = {
foundingDate: 2021,
team: {
marketing: [
{
name: "이진아",
salary: 3_250_000,
},
{
name: "박연성",
salary: 2_600_000,
},
],
design: {
ui: [
{
name: "송우진",
salary: 3_920_000,
},
{
name: "김지평",
salary: 2_743_000,
},
],
uxd: [
{
name: "이수아",
salary: 4_000_000,
},
{
name: "최철상",
salary: 3_208_000,
},
{
name: "고요미",
salary: 2_106_000,
},
],
},
},
};
for
문으로 돌아서 해당 객체들의 value
을 전부 일일히 분해해서 얻는건 상당한 고역일것이다for(let value in randomUser){
if(Object.prototype.hasOwnProperty.call(randomUser,value)){
const L1 = randomUser[value];
console.log('Level 1 : ' + value);
if(typeof L1 === 'object'){
for(let value in L1){
if(Object.prototype.hasOwnProperty.call(L1,value)){
const L2 = L1[value];
console.log('\tLevel 2 : ' + value);
if(typeof L2 === 'object'){
... 이하생략 🫣
}
}
}
}
}
}
object
의 value
를 한번에 찾을려고하면 for문
보다 재귀함수를 사용해서 자기자신을 직접호출하는 편이 훨씬 더 코드도 간결해지고, 가독성도 높아진다.const getAllValues = (department) => {
let valuesArray = [];
for (let key in department) {
if (Object.prototype.hasOwnProperty.call(department, key)) {
let value = department[key];
if (typeof value === "object" && !Array.isArray(value)) {
valuesArray = valuesArray.concat(getAllValues(value));
} else if (Array.isArray(value)) {
value.forEach((item) => {
valuesArray = valuesArray.concat(getAllValues(item));
});
} else {
valuesArray.push(value);
}
}
}
return valuesArray;
};
for in
문으로 해당 객체의 프로퍼티를 순회한다hasOwnProperty
로 해당 객체가 프로퍼티를 가지고 있는지 확인하고 valuesArray
에 getAllValues(value)
함수를 재귀적으로 호출하며 찾아나간후 concat
한다.forEach
를 돌아 다시 getAllValues(item)
을 재귀적으로 호출해 배열에 담아나간다.getAllValues()를 사용해 중첩객체들의 value들만 모인 배열을 만든 모습