[210325] TIL

19664 ワード

さいきかんすう


に質問自然数のリストを入力し、戻りリストの和の関数arrSum을を書いてください.
上記の質問をされると、今では簡単に答えを思いつくことができます.繰り返し文を使用して、結果値を入れる変数を宣言し、0を割り当て、指定した数値を順次入力します.以下をコードで表す.
function arrSum(arr) {
  let sum = 0;  // 결과 값을 넣을 변수 sum에 0을 할당
  for (let i = 0; i < arr.length; i++) { // 이제 한 쪽 눈 감고 쓸 수 있는 for문
    sum += arr[i]; // 변수로 선언해둔 sum에 arr의 첫 요소부터 끝까지 더해 준다.
  }
  return sum; // sum 리턴
}
このように繰り返し問題を解決するのは、よくないわけでもないし、よく使わないわけでもない.しかし、問題に直面したとき、私に選択させる方法はたくさんあります.これは決して不利なことではありません.大変な技術とはいえ、実際には特別な技術というよりは、開発者として当然の論理的アプローチのようです.
再帰関数は,問題を細分化することによって問題に近づく.
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;
[10,3,6,2]という配列の要素の和を求めるためには,一度に4つの数字を加えることができるが,[3,6,2]の和は10を加えることができる.では[3.6.2]の和は?[6,2]の和に3を加えればよい.では[6.2]の和は?[2]プラス6でいいです.
このように問題を解決する場合、同じ方法で(構造が同じ)より小さい場合を解決することができ、問題を解決する方法を再帰(recursion)と呼ぶ.
再帰的に解決すべき問題が発生した場合,考慮すべきステップは4つある.△今日初めて遭遇した再帰関数の問題が比較的解決しやすいのは,良好なペアリングに遭遇し,首都コードでこの4つのステップを記述したためである.
  • 再帰関数を定義する入出力値
  • 最初のステップは、問題が明確に表示されるように、入力値と出力値のタイプを定義します.首都コードを作成する場合は、以下のようにしてみます.
    //숫자를 입력받아 숫자를 출력할 때
    num => num
    //문자열이 들어 있는 배열을 입력받아 문자열을 출력할 때
    [string] => string
    //배열을 입력받아 배열을 출력할 때
    [] => [] //또는
    arr => arr
    学んだ知識を応用していますが、そうすれば、問題を解決する方法がより明確になります.
  • 個の問題を区分し、インスタンス数
  • を区分する.
    問題を解くとき、よくこの段階を漏らす.このステップは、[10,3,6,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;
    最初の段階でどのような変化が発生し、それからどのような変化が発生し、それから...このように問題を考え、問題をよく観察すれば、解決策が見えます.
  • 簡単なトラブルシューティング
  • 第2段階で最も簡単な問題から解決する.これを再帰の基礎(basecase)と呼び,再帰の脱出条件である.
    △実は、このbasecaseを見つけたのは第2段階だと思っていたので、第2段階をスキップしました.
    arrSum: [number] => number
    arrSum([ ]) = 0 //빈 배열일 경우 값이 0이라는 사실이 재귀의 탈출 조건이 된다.
    arrSum([e1, e2, ... , en])
  • 複雑な問題を解決する
  • 復帰を通じて残りの問題を解決する.第3段階まで方向を見失わなければ,関数は自己を呼び出して再帰を繰り返すことで残りの問題を解決する方法で構築される.
    function recursive(input1, input2, ...) {
      // 재귀의 기초 (base case)
      if (문제를 더 이상 쪼갤 수 없을 경우) {
        return 단순한 문제의 해답;
      }
      // recursive Case
      // 그렇지 않은 경우
      return 더 작은 문제로 새롭게 정의된 문제
      // 예1. someValue + recursive(input1Changed, input2Changed, ...)
      // 예2. someValue * recursive(input1Changed, input2Changed, ...)
    }
    上記の形式は一般的な再帰関数形式である.

    上記の手順で解いたいくつかの問題を記録します。


    アレイ要素の積

    function arrProduct(arr) {  // [] => num //배열을 입력받아 숫자출력
      if (!arr.length) {  // arr === [];  return 1
        return 1
      }
    
      const head = arr[0];   // head를 arr[0], tail을 arr.slice(1)로 선언
      const tail = arr.slice(1);
      return head * arrProduct(tail) // return 
    }

    配列内の要素の順序が逆になる

    function reverseArr(arr) { // arr => arr // 배열을 입력받아 순서가 뒤집힌 배열 출력 
      if (!arr.length) { // arr가 빈 배열이면 빈 배열 출력 // 재귀의 탈출조건
        return [];
      }
      const head = arr[0]; // 첫 번째 요소
      const tail = arr.slice(1); // 첫 번째 요소를 자른 나머지 요소
      return reverseArr(tail).concat(head); 
      // tail에서 가장 앞 요소를 잘라서 head에 concat한다.
      // ex) [1, 2, 3, 4, 5] => [2, 3, 4, 5] , [1]
      //     [2, 3, 4, 5], [1] => [3, 4, 5], [2, 1]
      //     [3, 4, 5], [2, 1] => [4, 5], [3, 2, 1]
      //     [4, 5], [3, 2, 1] => [5], [4, 3, 2, 1]
      //     [5], [4, 3, 2, 1] => [5, 4, 3, 2, 1]
    }

    ネストされた配列と文字列の入力を受け入れて、文字列が存在するかどうかを確認します。

    function unpackGiftbox(giftBox, wish) {
      // if (!giftBox.length || wish === '') {
      //   return false;
      // }
      
      for (let el of giftBox) { // giftBox배열 안에서 모든 요소를 찾는 반복문
        if(el === wish) {
          return true; // 그 요소가 wish와 같은지 확인한다.
        } else if (Array.isArray(el)) { // 요소가 배열이면
          if (unpackGiftbox(el, wish)) { // 그 배열 안에 들어가서 다시 unpackGiftbox호출(요소였던 배열, wish는 그대로)
          return true;
          }
       }
    } return false;
    }