재귀함수 Recursion
Achivement Goals
✅ 재귀의 의미에 대해서 이해하고, 자바스크립트에서 재귀 호출을 할 수 있다.
✅ 재귀를 언제 사용해야 하는지 알고 있다.
✅ 재귀적 사고 연습을 통해 재귀 함수를 base case와 recursive case로 나눠서 작성할 수 있다.
✅ 자료 구조 중 Tree 구조에 재귀 함수를 사용하는 이유를 이해할 수 있다.
✅ 실생활에 사용되는 유용한 Tree 구조를 알고 있다.
✅ 깊이를 알 수 없는 Tree 구조에 재귀 함수를 활용하여 모두 순회(traverse)할 수 있다.
📌 재귀란?
어떤 문제를 해결할 때, 동일한 구조의 더 작은 문제를 해결함으로써 주어진 문제를 해결하는 방법이다.
--> 처음 접했을 때는 이게 무슨말이야 ...? 했는데 공부해보니까 알겠다.
--> 코드를 짜는 게 중요한 게 아니고 짜기 전에 생각하는 게 중요하다.!
--> 로직을 충분히 생각한다음에 짜야 문제가 풀림. 에라 모르겠다 코드 쓰자~! 하면 절.대. 안풀림
📌 재귀를 이용해 JSON.stringify( ) 메서드 구현하기 --> 시도했으나 실패 ~! ~!
function stringifyJSON(obj) {
// obj가 숫자일 때
if(typeof obj === 'number'){return String(obj);}
// obj가 boolean일 때
if(typeof obj === 'boolean'){return String(obj);}
// obj가 null일 때
if(obj === null){return String(obj);}
// obj가 문자열일 때
if(typeof obj === 'string'){return `"${obj}"`;}
// obj가 배열일 때
if(Array.isArray(obj)){
let result = [];
for(let el of obj){
result.push(JSON.stringify(el));
}
return `[${result}]`
}
// obj가 객체일 때
else{
let finalStart = "{";
let finalLast = "}";
let str = '';
for(let key in obj){
if(typeof obj[key] === 'function' || obj[key] === undefined){
continue;
}
str += ('\"' + key +'\"');
str += ':';
if(typeof obj[key] === 'boolean' || obj[key] === null){
str += obj[key];
}
else if(Array.isArray(obj[key]) || typeof obj[key] === 'object'){
str += stringifyJSON(obj[key]);
}
else{
str += ('\"' + obj[key] +'\"');
}
str += ',';
}
return finalStart + str.slice(0, -1) + finalLast;
}
};
사실상 재귀는 거의 사용하지 않았다 ㅎㅎㅎㅎㅎㅎㅎ 💦💦
배열에서 JSON.stringify( )를 사용한 게 마음이 상해, 객체만은 재귀 함수를 쓰려고 엄청 노력했다.
결과적으로 JSON.stringify( ) 메서드를 사용한 듯한 결과를 도출해냈다 !!
📌 재귀를 이용해 JSON.stringify( ) 메서드 구현하기 --> 리팩토링 해보자 ~~~!!
function stringifyJSON(obj) {
// obj가 숫자일 때
if(typeof obj === 'number'){return `${obj}`;}
// obj가 boolean일 때
if(typeof obj === 'boolean'){return `${obj}`;}
// obj가 null일 때
if(obj === null){return `${obj}`;}
// obj가 문자열일 때
if(typeof obj === 'string'){return `"${obj}"`;}
// obj가 배열일 때
if(Array.isArray(obj)){
const newArr = obj.map(el => {
return stringifyJSON(el);
});
return `[${newArr}]`;
// obj가 객체일 때
}else{
let newObj = '';
for(let key in obj){
if(obj[key] === undefined || typeof obj[key] === 'function'){continue;}
else{
newObj = newObj + `${stringifyJSON(key)}:${stringifyJSON(obj[key])},`;
}
}
return `{${newObj.slice(0, -1)}}`;
}
};
📌 재귀를 이용해 JSON.stringify( ) 메서드를 구현해보며 알게된 점
JSON.stringify( )는 정말 모~든 것을 문자열로 바꿔준다.
코드를 구현한 걸 보면 알겠지만, 숫자 타입, boolean 타입, null 타입, 문자열 타입, 배열과 객체까지 문자열로 반환한다.
원시타입은 해당 타입에 " " 큰 따옴표를 씌워주면 그만이었다.
※문자열 타입은 해당 문자열에 따옴표 한번 더 씌우기 주의※
💚 배열과 객체 --> 객체타입이 문제였다 ^^
01 배열은 문자열과 합쳐지면 없어지기 일쑤
02 객체는 [object Object]를 출력하기 일쑤
일단, 배열과 객체 내에 있는 요소나 키-값 쌍들은 재귀함수를 써서 모두 문자열로 바꿔주고,
배열 리터럴( [ ] )과 객체 리터럴( { } )도 값을 반환하기 직전에 씌워주면 그만이었다.
문제는, 빈 배열이나 빈 객체를 처리하는 방식이었다. 이걸 해결하면서 알게된 점이 많다.
💚 일단 첫 번째, 빈 배열은 배열의 요소에 ( 요소가 없긴하지만 ) 아무리 undefined를 입혀도 빈배열이다.
----------------------------------------------
-----------------------------------------------
-----------------------------------------------
stringifyJSON( )함수에 빈 배열을 담아 호출하더라도 빈 배열이 그대로 나오는 걸 확인할 수 있다.
배열은 이렇게 map을 통해 모든 요소값에 접근하여 stringifyJSON( ) 함수를 입혀 모두 문자열로 변환해, 새로운 배열에 담아줬다.
새로운 배열은 마지막에 `[ ${ } ]` 틀을 씌워줌으로써 배열 자체도 문자열로 변환해주었다.
💚 자 두번째, 빈 객체는 for~in문을 거치지 않는다 !! 키 값이 없기때문에 접근 조차 불가하다
------------------------------------------------
지금 for~in문 안에서 빈 객체 아무 반응 없는 거 보이시나여,,,
for~in문은 빈 객체를 실행하지 않는다는 걸 알 수 있다 !!!
모든 키-값 쌍을 거치는 for~in문을 PASS하고 바로 '{ }'를 리턴해야함을 예상할 수 있다.
또한 객체는 배열처럼 map과 같은 메소드도 없어서 직접 "키-값 형식"과 "쉼표"까지 만들어 붙여야 한다.
따로 빈 문자열이 할당된 새로운 변수를 선언해주고, 해당 변수에 " 키 : 값 , " 세트를 계속해서 더해줬다.
이렇게 모두 더해서 만들어진 변수는 마지막에 쉼표 하나를 더 달고 나온다. 이를 slice(0, -1)로 간단히 제거해준다.
정리된 변수는 마지막에 `{ ${ } }` 틀을 씌워줌으로써 객체 자체도 문자열로 변환해주었다.
📌 느낀 점
재귀를 써야한다는 강박에 싸여, 어떻게 하지?!...어떻게 하지?!?! 초조해했었다.
함수를 직접 만들고 호출하는 방식이 아직 익숙하지 않다. 이 함수... 써도 되는걸까? 하는 스스로에 대한 믿음도 부족하고 ㅋㅋㅋ
console.log 무한으로 찍어보고 코드도 다시 구성해보면서 두려움이 많이 줄었다.
뇌에 익도록 자주 연습해야겠다.