トラック
/
JavaScript
JavaScript
/
演習
/
ラザニアの達人
ラザニアの達人

ラザニアの達人

学習演習

はじめに

関数とは、何らかの処理を行うために使われる、整理された再利用可能なコードのブロックです。 JavaScriptで関数を定義する方法はいくつかあります。 ここでは、_関数宣言_と_関数式_を見ていきましょう。 アロー関数のようなそのほかの方法は、別のコンセプトで扱います。

関数宣言

JavaScriptで関数を定義する標準的な方法は、_関数宣言_です。_関数定義_や_関数文_と呼ばれることもあります。

functionキーワード、関数名、そして丸括弧(())で囲んだカンマ区切りの仮引数のリストからなります。 そのあとに、波括弧({})で囲まれた関数本体(関数が何をするかを定義する文の集まり)が続きます。

function someName(param1, param2, param3) {
  // ...
}

JavaScriptでは、関数名のあとに、引数を入れた括弧を続けて書くことで、関数を呼び出します(invoke)。

someName(arg1, arg2, arg3);

仮引数

関数本体の中で仮引数を扱うときは、副作用が起こりうることに気をつけましょう。

  • プリミティブ型の値は_変更できません_。 関数本体で引数に何が起きても、元の値が影響を受けることはありません。
  • そのほかのすべての値(オブジェクト、配列、関数)では、再代入しても元の値は変わりません。 ただし、そのような引数を変更すると(たとえばオブジェクトにキーを追加すると)、渡された元の値も変更されます。

JavaScriptでは、デフォルトで、関数宣言で定義したすべての仮引数は省略可能です。 仮引数の数より少ない引数しか渡さないと、足りない引数は関数の中でundefinedになります。詳しくはnullとundefinedを参照してください。 多くの場合、undefinedよりも適切なデフォルト値を割り当てるのがよいでしょう。 これは、関数定義で仮引数にデフォルト値を直接指定することで行えます。

function someName(param1 = defaultValue1, param2 = defaultValue2) {
  // ...
}

return文

return文は、関数の実行を終了し、関数の呼び出し元に返す値を指定します。1つの関数に複数のreturn文を書くこともできます。

function checkNumber(num) {
  if (num === 0) {
    return 'You passed 0, please provide another number.';
  }

  return 'Thanks for passing such a nice number.';
}

関数の戻り値は、変数に代入できます。

function sum(x, y) {
  return x + y;
}

const total = sum(5, 10);
// => 15

値をreturnしない関数や、return文を持たない関数の結果はundefinedです。 JavaScriptには暗黙のreturnはありません。

function nakedReturn(a) {
  a * 2;
  return;
}

nakedReturn(1);
// => undefined

function noReturn(a) {
  a * 2;
}

noReturn(1);
// => undefined

JavaScriptでは、値を1つだけしか返せません。 もっと多くの情報を渡したいときは、まずそれらを1つにまとめる必要があります。ふつうはオブジェクトか配列にまとめます。

function divide(a, b) {
  return {
    quotient: Math.floor(a / b),
    remainder: a % b,
  };
}

関数式

関数宣言は、独立した文です。 しかし、別の式の一部として関数を定義すると便利なこともあります。たとえば、代入の中や、仮引数(コールバック)として、あるいはオブジェクトの値としてです。 これは関数式で行えます。 構文は関数宣言と同じですが、関数名を省略して_無名関数_を作れる点だけが異なります。

const someFunction = function (param) {
  // ...
};

someOtherFunction(function (param) {
  // ...
});

const obj = {
  someFunction: function (param) {
    // ...
  },
};

説明

この演習では、お気に入りのレシピ本にある最高のラザニアの準備と調理に関するコードを、もう少し書いていきます。

タスクは5つあります。 最初のタスクは調理そのものに関するもので、残りの4つは完璧な準備に関するものです。

1. ラザニアができあがったかどうかを判定する

ラザニアをオーブンに入れているときは、もう取り出せるかどうかを知りたくなります。 ラザニアがオーブンで焦げないように、ふつうはタイマーをセットします。 でも、ときどきそれを忘れてしまいます。

タイマーの残り時間を分単位で入力として受け取る関数cookingStatusを書いてください。 この関数には、3つの結果がありえます。

  • タイマーが0を示していれば、'Lasagna is done.'を返します。
  • タイマーがそれ以外の数値を示していれば、結果は'Not done, please wait.'になります。
  • タイマーの値を指定せずに関数を呼び出した場合、結果は'You forgot to set the timer.'になります。

タイマーが0未満の値を示すことはありません。

cookingStatus(12);
// => 'Not done, please wait.'

cookingStatus();
// => 'You forgot to set the timer.'

2. 準備にかかる時間を見積もる

次に作るラザニアのために、料理を楽しめるように、十分な時間を確保しておきたいものです。 ラザニアに入れる層の計画はもう立ててあります。 そこで、それをもとに準備にどれくらい時間がかかるかを見積もりたくなります。

層の配列と、層1つあたりの平均準備時間(分)を入力として受け取る関数preparationTimeを実装してください。 この関数は、層の数をもとに、準備にかかる合計時間の見積もりを返します。 平均準備時間を指定せずに関数を呼び出した場合は、代わりに2分とみなします。

const layers = ['sauce', 'noodles', 'sauce', 'meat', 'mozzarella', 'noodles'];
preparationTime(layers, 3);
// => 18

preparationTime(layers);
// => 12

3. 必要な麺とソースの量を計算する

時間を確保するだけでなく、夢のラザニアを作るのに十分なソースと麺があることも確かめたいものです。 ラザニアの麺の層1つにつき、麺が50グラム必要です。 ソースの層1つにつき、ソースが0.2リットル必要です。

層の配列を入力として受け取る関数quantitiesを定義してください。 この関数は、料理に必要な麺とソースの量を求めます。 結果は、noodlesとsauceというキーを持つオブジェクトとして返します。

quantities(['sauce', 'noodles', 'sauce', 'meat', 'mozzarella', 'noodles']);
// => { noodles: 100, sauce: 0.4 }

4. 秘密の材料を加える

少し前、友人を訪ねてそこでラザニアを食べました。 それは素晴らしく、何か特別なものがありました。 友人は材料のリストを送ってきて、リストの最後の項目が、その料理をあんなに特別にした「秘密の材料」なのだと教えてくれました。 そこで、自分のレシピにもその秘密の材料を加えたくなります。

材料の配列を2つ入力として受け取る関数addSecretIngredientを書いてください。 1つ目の入力は友人が送ってきたリストで、2つ目は自分のレシピの材料リストです。 この関数は、友人のリストの最後の項目を、自分のリストの末尾に追加します。 自分のレシピを表す配列は直接変更し、関数は何も返しません。 ただし、1つ目の入力は変更しないでください。

const friendsList = ['noodles', 'sauce', 'mozzarella', 'kampot pepper'];
const myList = ['noodles', 'meat', 'sauce', 'mozzarella'];

addSecretIngredient(friendsList, myList);
// => undefined

console.log(myList);
// => ['noodles', 'meat', 'sauce', 'mozzarella', 'kampot pepper']

5. レシピの分量を調整する

レシピ本に載っている分量では、ラザニアが2人分しかできません。 次はもっと多くの人のために料理したいので、人数に応じた分量を計算したくなります。

2つの入力を受け取る関数scaleRecipeを実装してください。

  • 2人分に必要な分量を持つレシピオブジェクト。 オブジェクトの形式は、以下の例で確認できます。
  • 作りたい人数。

この関数は、作りたい人数に必要な分量を持つレシピオブジェクトを返します。 ただし、元のレシピはそのままにしておきたいものです。 つまり、このタスクではレシピの入力は変更しないでください。

const recipe = {
  noodles: 200,
  sauce: 0.5,
  mozzarella: 1,
  meat: 100,
};

scaleRecipe(recipe, 4);
// =>
// {
//   noodles: 400,
//   sauce: 1,
//   mozzarella: 2,
//   meat: 200,
// };

console.log(recipe);
// =>
// {
//   noodles: 200,
//   sauce: 0.5,
//   mozzarella: 1,
//   meat: 100,
// };
GitHubで編集する リンクは新しいウィンドウまたはタブで開きます
JavaScript Exercism

ラザニアの達人を始める準備はできましたか?

Exercismに登録すれば、37個のコンセプト159個の演習、そして本物の人間によるメンタリングとともに、JavaScriptを学んでマスターできます。すべて無料です。