LoginSignup
2
1

More than 3 years have passed since last update.

[Javascript]おばあとおじいの1週間の献立を無理やり再帰にする準備をする

Last updated at Posted at 2020-07-01

背景

「どうせ学んでいるのならアウトプットせい!」とお叱り愛の叱咤激励をもらいました。
ごもっともなので毎日投稿を習慣化してみる。

自己紹介(=情報の信頼度)

2020年4月から学習をはじめた駆け出しエンジニア
HTML、CSS、Javascript、React、Vue、Laravelを学習
AWSもamplifyとかS3、EC2あたりをちょこっと。ふくおか。

今日のおはなし

〜おじいおばあの家〜

おばあ「今週のごはんは何にしようか。」

おじい「精のつくものとらねばねえ。」

おじい「しかし、ついつい昨日のごはんも忘れてしまうとよ。困ったもんちゃ!」

おばあ「では、私が「1週間の献立」をつくってみようかね」

おばあ「最近、ぷろぐらみんぐ教室で学んだの。」

お題

[朝、昼、夜]の献立表をつくる。

献立表=[
[🍛、🍛、🍛][🍛、🍛、🍣][🍛、🍛、🍜]
[🍛、🍣、🍛][🍛、🍣、🍣][🍛、🍣、🍜]
[🍛、🍜、🍛][🍛、🍜、🍣][🍛、🍜、🍜]
[🍣、🍛、🍛][🍣、🍛、🍣][🍣、🍛、🍜]
[🍣、🍣、🍛][🍣、🍣、🍣][🍣、🍣、🍜]
[🍣、🍜、🍛][🍣、🍜、🍣][🍣、🍜、🍜]
[🍜、🍛、🍛][🍜、🍛、🍣][🍜、🍛、🍜]
[🍜、🍣、🍛][🍜、🍣、🍣][🍜、🍣、🍜]
[🍜、🍜、🍛][🍜、🍜、🍣][🍜、🍜、🍜]
]

※おじいさんとおばあさんは、カレー、寿司、ラーメンしかたべません。

再帰を学ぼう!

再帰とは

関数が自分自身を呼ぶこと。
たとえば、繰り返し処理(forループ)を、
Screen Shot 2020-07-01 at 21.44.29.png
こう書き換えられる。
Screen Shot 2020-07-01 at 21.45.47.png

この場合、
n==3であれば、x*pow(2,3) //-> 2*2
n==2であれば、x*pow(2,3) //-> 2*2*2
n==1になるとき、x*pow(2,3)//-> 8

となっている。

@fujitanozomu様にご指摘いただきました!ありがとうございます!

0乗が正しく求まらないので書き換えられていないのでは

仰られる通りです。ご指摘いただきありがとうございます。
下記に訂正いたします。

function pow(x, n) {
  if (n == 0) {
    return 1;
  } else {
    return x * pow(x, n - 1);
  }
}

再帰を考えるときのコツ

再帰を終了する条件:ベースケース
再帰を継続する条件:リカーシブケース
と呼ぶらしい。

再帰関数自体にベースケース、リカーシブケースに関係する値を引数としていれておくこと。

実際に再帰で考えてみよう!

おばあ「まず、全体の流れを理解しよう。」

おばあ「すべての献立パターンがつくれて、関数を実行すると「献立表」が返ってくる。」

おばあ「食べ物は、食糧庫からとってこよう。」

おばあ「朝、昼、夜、の3食決まったら再帰は終了だね。間食はしないからね」

おばあ「じゃあ、まだ朝、昼までしか決まっていないときは、再帰を継続する必要があるね。」

Screen Shot 2020-07-01 at 22.55.24.png
おばあ「あ、再帰したときに、減らす引数がない!」
Screen Shot 2020-07-01 at 22.55.03.png

おばあ「これでよしっと。」

おばあ「繰り返し処理になっている部分は、、と。」

おばあ「各食事に対してpantryからすべての食事を取り出す。」

おばあ「まず、[🍛、🍛、🍛]をつくるには、」

おばあ「1回目の食事を[🍛]いれて、2回目の食事にする。」

おばあ「2回目の食事を[🍛]いれて、3回目の食事にする。」

おばあ「3回目の食事を[🍛]いれて、3食分の献立をresultにいれる。」

おばあ「このあたりが繰り返しの処理っぽいな。」

おばあ「次に、[[🍛、🍛、🍣]をつくる。]

おばあ「途中まで[🍛、🍛]をつくってるから、その上に対して🍣を入れたいけど、どうしようか」

おばあ「作成途中の献立[🍛、🍛]を受けとれるようにしよう。」

おばあ「作成途中の献立を受け取れるように引数を変更して、」

おばあ「それらに対してpantryから次の食事を加えよう。」
Screen Shot 2020-07-01 at 22.53.25.png

おばあ「これで、自分自身を呼ぶことで、配列を作るrecuse関数ができあがった」

おばあ「あ。recuse関数の発火をしてない。えい。」

const menu = (numberOfMeals=3) => {
  // Your code here
  const pantry = ["🍛", "🍣", "🍜"];
  let result = [];
    function recurse(meal=[],numberOfMeals) {
    if (numberOfMeals > 0) {
      //再帰の処理
      for (let i = 0; i < pantry.length; i++) {
       recurse(meal.concat(pantry[i]),numberOfMeals - 1,) 
      }
    } else {
      //3食分の献立ができたので終了する
      result.push(meal)
    }
    return result;
  }
  return recurse([],numberOfMeals)
};

その後

おばあ「meal()[1]」

おじい「もぐもぐ」

おばあ「meal()[4]」

おじい「もぐもぐ」

補足

pushではなく、concatで記述している理由は、配列のままにしたいからです。

pushだと要素数を返す
=>数字になる。
=>昼のごはんが選べなくなる
=cannnot read proptyとなるため避けています。

recuse関数外にresultを定義しているのはクロージャーを使いたいためです。

後日記事にします。

解釈間違い等、何かあればぜひコメントください。

誰か再帰の使いみちを教えてください・・・。

2
1
16

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1