[SEB FE] Section3 unit 1 재귀함수 문제풀이

동화·2022년 10월 20일
0

코드스테이츠

목록 보기
22/32

sumTo

수(num)를 입력받아 1부터 num까지의 합을 리턴해야 합니다.

function sumTo(num) {
  // base case -> num === 0
  // 0 + sumTo(num-1)
 
 return num === 0 ? 0 : num + sumTo(num-1)
}

물론 base case와 따로 처리해도 상관없는데
그냥 if - else 로 하고 보니 삼항연산자를 이용해야겠다 싶어서 이용..해도 되려나? 했는데 돼서 그냥 됐다 ! 됐어!🤪




isOdd

수를 입력받아 홀수인지 여부를 리턴해야 합니다.

function isOdd(num) {
  // num > 0 => num에 계속 -2를 해주어서 0이 되면 짝수(false), 1이 되면 홀수(true)
  // num < 0 => num에 계속 +2를 해주어서 0이 되면 짝수(false), 1이 되면 홀수(true)
  // -17도 홀수, -40도 짝수 (음수에도 짝수 홀수가 있음!)

  if (num === 0) return false // base case
  if (num === 1) return true  // base case

  if (num < 0) {
    return isOdd(num + 2)
  }
  if (num > 0) {
    return isOdd(num - 2)
  }
}

처음에 약간 오잉..? 싶었음
항상 -2 를 반복해서 빼주는 것이 더 익숙하던 (반복문) 나는... 이걸 어떻게 처리해야 하나 고민했다
재귀함수가 익숙하지가 않아서 ㅠ_ㅠ
결국 노트를 펴서 정리해서 적어놓기 시작함.

 num === 9
 9 -2 -2 -2 -2 = 1
 7    -2 -2 -2 = 1
 5       -2 -2 = 1
 3          -2 = 1

약간 반복문은 -2를 반복해서 빼주는 게 중점이라고 한다면,
재귀함수는 내가 받은 수 자체에서 하나씩 덜어내어 결론에 다다르게 하는..?
뭐랄까 설명하기가 좀 애매한데 계속 한 번 처리한 것을 묶어나가는 부분을 계속 생각하면 쉽게 풀리는 것 같다.. 근데 이것도 걍 내 머릿속에서 나온거라 설명이 애매함ㅋㅋㅋㅋ




factorial

수를 입력받아 n-factorial(n!; 엔-팩토리얼) 값을 리턴해야 합니다.

function factorial(num) {
  // 1 * 2 * 3 * ... * num-1 * num
  // base case = 1

  if (num === 1 || num === 0) return 1
  else return num * factorial(num-1)
}

팩토리얼은 예제에도 나와있던 거니까 비교적 쉽게...




fibonacci

수(num)를 입력받아 피보나치 수열의 num번째 요소를 리턴해야 합니다.

function fibonacci(num) {
  // 피보나치 수열 = 0,1,1,2,3,5,8,13...
  // num === 0 : 0
  // num === 1 : 1
  // (예) num = 6 -> 8 (3+5 = (num=4) + (num=5))
  // num === (num-1) + (num-2) ????

  if (num === 0) return 0
  if (num === 1) return 1

  if (num >= 2) {
    return fibonacci(num-1) + fibonacci(num-2)
  }
}

피보나치는 근데 약간 기계처럼 했던게, 반복문에서도 그렇고 0, 1 은 그냥 일단 세팅해야겠다라는 생각이 자리잡혀 있어서 ㅋㅋㅋㅋ 그렇게 하고 났더니 수월하게 풀렸다




arrSum

배열을 입력받아 모든 요소의 합을 리턴해야 합니다.

function arrSum(arr) {
  // arr[0] + arr[1] + ... + arr[n-1]
  // arr[0]를 제외한 배열 -> arr.slice(1)
  // arr.slice(1) = arr[0] + arr[1] + arr.slice(2)
  // arr[1] + arr.slice(2) 이건 arr[0] 을 제외한 새로운 배열? 의 arr[0] + arr.slice(1) 과 같음
  
  if(arr.length === 0) return 0
  
  return arr[0] + arrSum(arr.slice(1))
}

이 문제는 내가 페어분한테 설명도 해줬었던 문제인데, 설명하기가 참 어려웠다.
결국에는 arr[0] 을 걷어낸 새 배열에서 또 arr[0]을 걷어낸 것이고 그것이 결국 원본 arr에서는 arr[1]이 되는 것.
이런식으로 걷어내어 더해주는 방식을 이용했다

function arrSum(arr) {
  if (arr.length === 0) {
    return 0;
  }

  // const [head, ...tail] = arr;
  const head = arr[0];
  const tail = arr.slice(1);
  return head + arrSum(tail);
}

레퍼런스 코드인데, 레퍼런스에서는 첫번째 인자와 슬라이스한 배열을 새로 변수로 지정해주었다.
이렇게 해야하는 건강..? 🧐

(곱하기는 같으니 생략)




arrLength

배열을 입력받아 그 길이를 리턴해야 합니다.

❌ 실패 코드

function arrLength(arr) {
  // arr.isEmpty() === true -> 빈배열의 길이는 0

  if (arr.isEmpty()) return 0

  let count = 0;
  if (!arr.isEmpty()) {
    count++
    arrLength(arr.slice(1))
  } return count
}

무슨 생각으로 이렇게 했는진 모르지만, 하나씩 썰어내어 그과정에서 카운트를 하려고 했던 모양
근데 사실 이렇게 풀수도 있지 않았을까? 했는데 결국 안돼서 그냥 쓸어 엎어버렸다

function arrLength(arr) {
  // arr.isEmpty() === true -> 빈배열의 길이는 0

  if (arr.isEmpty()) return 0

  return 1 + arrLength(arr.slice(1))

}
//  arr = [5,6,7,8]
// 1 + arrLength([6,7,8])
// 1 + 1 + arrLength([7,8])
// 1 + 1 + 1 + arrLength([8])
// 1 + 1 + 1 + 1 + arrLength([])
// 1 + 1 + 1 + 1 + 0
// 답은 4

수도코드 쓰는 거 자체가 별로 습관이 안되어있어서,
그냥 일단 코드쳐보고 확인해보는 게 익숙했는데 재귀함수는 손으로 직접 써봐야 이해가 된다
아직은 많이 부족한지.. 그래도 수도코드를 쓰는 게 좋다고는 하니 열심히 쓰는 중!




drop

수(num)와 배열을 입력받아 차례대로 num개의 요소가 제거된 새로운 배열을 리턴해야 합니다.

function drop(num, arr) {
  // arr에서 num개의 요소가 제거된 새로운 배열
  // num = 2 , arr = [1,2,3,4]. -> [3,4]

  if (num >= arr.length) return []
  if (num === 0) return arr

  arr.shift()             // 앞에 거 하나 제거해줌
  return drop(num-1, arr) // 제거된 나머지 arr, 하나 제거했으므로 num에서 1 빼줌
}

내가 쓴 답.
약간 헤맸다.. 위에서 계속 slice를 사용했기때문에.. shift도 생각해!

function drop(num, arr) {
  // if (num >= arr.length) {
  //   return [];
  // }

  if (num === 0 || arr.length === 0) {
    return arr;
  }

  // const [head, ...tail] = arr;
  const tail = arr.slice(1);
  return drop(num - 1, tail);
}

근데 레퍼런스 코드에는 위에 내가 했던 부분이 주석처리가 되어있었다
그 부분이 num === 0 과, 배열의 길이가 0이거나 할 때를 arr로 출력하도록 되어있었음
저 부분을 처리하지 않아도 되는 거였을까?

스터디원 답변 😈

  • num 이 arr.length 보다 크면 하나씩 빠지고 빠지면서 arr이 빈배열이 돼서 그걸 리턴하게 된 것.
if  (num >= arr.length) {
  return []; // num이 arr.length보다 같거나 크면 이미 arr은 []이기 때문에 return arr과 같다.
             // 위에 arr.length === 0 조건이 num >= arr.length와 같은 의미
}
if (num === 0) {
  return arr; // 입력된 arr 그대로 리턴
if (num >= arr.length) {
return arr;
}
if (num === 0) {
return arr;
}


if(arr.length === 0) { // arr.length === 0 과 num >= arr.length는 arr이 []일 경우를 표현하는 같은 조건
return arr;
}
if(num === 0) {
return arr;
}


if(arr.length === 0 || num === 0) {
return arr;
}



take

❌ 실패 코드

function take(num, arr) {
  // arr에서 num개의 요소만 포함된 새로운 배열
  // num = 2 , arr = [1,2,3,4] -> [1,2]

  if (num >= arr.length) return arr
  if (num === 0) return []

  arr.pop()
  return take(num-1, arr)
}

이 문제는 오히려 방금 shift를 하고 와서 slice를 잊은 케이스 ^^...
머리를 다양하게 굴려보렴..!
아무튼 pop pop 의 굴레에 빠져서 이게 왜 안되는가에 허우적 대고 있음.
사실 아직도 모르겠다.. 스터디에 물어봐야겠둠..


❌ 실패 코드 2

function take(num, arr) {
  // arr에서 num개의 요소만 *순차적으로* 포함된 새로운 배열
  // num = 2 , arr = [1,2,3,4] -> [1,2]

  if (num >= arr.length) return arr
  if (num === 0) return []

  let arr2 = arr[0]
  return arr2.push(take(num-1, arr.slice(1)))
}

이건 좀 부끄러운 ... 코드인데 왜냐면 출력값이 배열인지 문자열인지 아주 바보바보같이 헷갈려했던 문제
arr[0]이면 문자열로 출력되며, 이것을 arr[0]으로 해봤자 문자열 하나가 변수에 지정되었을 뿐임
그러니 문자열 하나에 백날 push 해봤자 배열이 아니니 추가가 될리가 만무함
게다가 push 는 새로운 배열을 반환하므로 문제(immutability)에 적합하지가 않다..

function take(num, arr) {
  // arr에서 num개의 요소만 *순차적으로* 포함된 새로운 배열
  // num = 2 , arr = [1,2,3,4] -> [1,2]

  if (num >= arr.length) return arr
  if (num === 0) return []

  return [arr[0]].concat(take(num-1, arr.slice(1)))
}

드디어 성공했다 ㅠㅠ .concat을 사용했음
레퍼런스는 arr[0]과 arr.slice(1)을 변수로 지정해주었고.. 나는 일단 냅다 적음
더 지저분한 거 같기도 하고.. ㅠ

아무튼 성공했다.




and

배열을 입력받아 모든 요소의 논리곱(and)을 리턴해야 합니다.

function and(arr) {
  // false 인 경우 = 하나라도 false이면..
  if (arr.length === 0) return true
  
  return arr[0] && and(arr.slice(1))
}

약간 문제 이해하는데에는 힘들었으나 뭔가 깨달음을 얻고 난뒤엔 허무하리 만큼 쉬웠다..ㅋ_ㅋ




or

배열을 입력받아 모든 요소의 논리합(or)을 리턴해야 합니다.

function or(arr) {
  // 하나라도 true 이면 true 
  // 전부 false 여야지만 false
  if (!arr.length) return false
  return arr[0] || or(arr.slice(1))
}

앞에 문제랑 비슷한듯 싶지만 이거 비밀인데 나는 조금 헤맸다.. ㅋ
왜냐하면.. 전부 false 여야지 false로 출력하는 걸로 풀었는데
답이 안나오더라 ㅠ
ㅠ 그래서 일단 페어분이 푸신 대로 ||로 풀었음




reverseArr

배열을 입력받아 순서가 뒤집힌 배열을 리턴해야 합니다.

function reverseArr(arr) {
  if (arr.length === 0) return []

  return reverseArr(arr.slice(1)).concat(arr[0])
}

마찬가지로 concat을 이용해서 배열로 만들어줌.
앞에서는 계속 더하기.. 라든지 사칙연산을 이요한 문제여서 그냥 더해줬는데
이렇게 배열로 출력하라고 할때는 concat을 이용하기..!
그리고 slice(-1)로 해서 시간초과가 나왔다.. ㅎ_ㅎ 왜 -1을 했을까 마지막 걸 빼는게 아닌데!
원본 배열의 첫번째요소를 마지막에 넣어야했던 것 뿐.. 차근차근..
머리로는 이해가 되는데 이걸 구현해 내는 과정에서 혼동이 있었나보다




findMatryoshka

러시아 전통인형 마트료시카에 대한 정보를 담은 객체와 수를 입력받아 조건에 맞는 인형이 있는지 여부를 리턴해야 합니다.

function findMatryoshka(matryoshka, size) {
  // matryoshka.size === size -> true ???
  // 처음 마트료시카 인형의 사이즈와 입력받은 사이즈가 다르면, (인형의 사이즈가 더 커야함)
  //    find마트료~함수를 다시 돌려준다. (인자는 mat~.mat~) ??
  //  아.. 그리고 mat.mat가 존재해야 하나? (->  matryoshka: null이면 안됨)

  if(matryoshka.size === size) return true
  else if(matryoshka.size > size && matryoshka.matryoshka) 
      return findMatryoshka(matryoshka.matryoshka, size)
  else return false
}

어찌 어찌 풀었다.. 마트료시카




unpackGiftbox

선물 상자에 대한 정보를 담은 배열과 문자열을 입력받아 조건에 맞는 선물이 있는지 여부를 리턴해야 합니다.

❌ 실패 코드 (잘 모르겠음..)

function unpackGiftbox(giftBox, wish) {
  // giftBox에 wish가 있는지 확인 -> 있으면 true
  // 만약 giftBox속에 또 giftBox박스가 있다면~ 거기서도 확인해 -> true
  // 그런데도 없으면 false???

  if (giftBox.length===0 || wish.length === 0) return false
  
  else if (giftBox.indexOf(wish)) return true
  else if (giftBox) .. ????????????????? ^^
}

약간 도저히 머리가 돌아가질 않아서.. ㅠ_ㅠ 레퍼런스를 보고 공부해보기로 했다

📌 레퍼런스 1

function unpackGiftbox(giftBox, wish) {
 // recursive case
 for (let i = 0; i < giftBox.length; i++) {	// giftbox 배열을 돌면서,	
   if (giftBox[i] === wish) {					// 일단 겉 배열에서 비교해서 true 출력
     return true;
   } else if (Array.isArray(giftBox[i])) {		// 만약 giftBox의 한 index가 배열일 경우에는,
     const result = unpackGiftbox(giftBox[i], wish); // 재귀함수를 이용하여 그 배열 자체를 새로운 배열 인자로 넣어준다
     if (result === true) { 	// 그래서 결과가 true 값이 나오면
       return true;			// 최종 결과로 true  출력
     }
   }
 }

 // base case
 return false;
}

📌 레퍼런스 2

function unpackGiftbox(giftBox, wish) {
 // recursive case
 let anotherBoxes = [];			// 또 다른 빈 배열 하나를 선언해준다
 for (let i = 0; i < giftBox.length; i++) {
   if (giftBox[i] === wish) {
     return true;					// giftbox의 한 원소가 wish 일 경우 true,
   } else if (Array.isArray(giftBox[i])) { 	// giftBox의 원소가 배열일 경우...
     anotherBoxes = anotherBoxes.concat(giftBox[i]);
   }					// 아까 선언해놨던 배열에 그 원소를 추가한다.
 }

 if (anotherBoxes.length > 0) {		// 그 배열이 존재하게 된다면...?
   return unpackGiftbox(anotherBoxes, wish); // 그 배열을 가지고 재귀함수를 돌려준다.
 }

 // base case
 return false;
}

📌 레퍼런스 3

function unpackGiftbox(giftBox, wish) {
 const result = giftBox.reduce((acc, curr) => {
   if (curr === wish) {
     return true;
   } else if (Array.isArray(curr)) {
     return unpackGiftbox(curr, wish) || acc;
   } else {
     return acc;
   }
 }, false);
 return result;
}

** 이해가 필요..




flattenArr

다차원 배열을 입력받아 1차원 배열로 변환하여 리턴해야 합니다.

📌 레퍼런스 1

function flattenArr(arr) {
 // base case
 if (arr.length === 0) {
   return [];
 }

 // recursive case
 const head = arr[0];
 const tail = arr.slice(1);
 if (Array.isArray(head)) {
   return flattenArr([...head, ...tail]);
 } else {
   return [head].concat(flattenArr(tail));
 }
}

📌 레퍼런스 2

function flattenArr(arr) {
 const result = [];
 for (let i = 0; i < arr.length; i++) {
   if (Array.isArray(arr[i])) {
     const flattend = flattenArr(arr[i]);
     result.push(...flattend);
   } else {
     result.push(arr[i]);
   }
 }
 return result;
}

📌 레퍼런스 3

function flattenArr(arr) {
 return arr.reduce(function (result, item) {
   if (Array.isArray(item)) {
     const flattened = flattenArr(item);
     return [...result, ...flattened];
   } else {
     return [...result, item];
   }
 }, []);
}

2개의 댓글

comment-user-thumbnail
2022년 10월 20일

블로깅에 정성이.. 가득가득하네요 저도 본받겠습니다 ^^

1개의 답글