前々回、関数はデータだから名前を付けて、好きな動かせることを見ました。 他のデータ同様に、関数に関数を入力してみましょう(次回は関数を出力してみます)。
値呼出し:関数への入力を値になるまで評価する呼び出し方です。
const loop = _ => loop(); // 無限ループする関数
loop; // エラーにはならない
loop(); // エラーになる:評価すると無限ループしてエラー
const one = _ => 1; // 引数を使わない関数
one(loop()); // エラーになる:値を入力しようとしてloop()を評価した
関数名(引数 ...)
とすると、関数の右辺が評価されるのでした。
では、関数を渡す方法について説明します。 今度は関数oneは「関数の出力値」でなく「関数の名前」が入力されます。 これを名前呼び出しといいます。
one(loop); // これがエラーにならないから
one(_=>loop()); // これもエラーにならない:上と同じ
(註)他にもyield などジェネレータやイテレータで使うまで評価を遅らせることもできます
こんな関数 time が作れたら便利ですよね。
// 使い方 1回だけ計る
time(_=> square(2000));
// 1000回分の平均時間
time(_=> square(2000), 1000);
中身はこんな感じになってます。
const time = (input, n = 1, average = 0.0) =>
{
for (let i = 0; i < n; ++i) // n回ループ
{
const start = window.performance.now(); // 現在時刻の取得
input(); // ここで入力の関数を動かす
const elapsed = window.performance.now() - start; // 掛かった時間
average += elapsed / n;
}
return average; // 戻り値
};
今までの関数より大きくて、新しい仲間がでてきました。
2行目 (input, n = 1, average = 0.0)
に =
がであります。
これはデフォルト引数といって、入力が指定されなかったときの値になります。
3行目 ... 最後の行の { ... return }
は、複数の命令と、返す(return)値を書く方法です。
4行目 for (let i = 0; i < n; ++i)
は再帰を使わずに n 回ループする方法です。
いままで通り再帰を使うとこんな感じでかけます。
// 再帰ver : for ループと比べてみよう
const time_rec = (process, n = 1, average = 0.0, i = 1) =>
{
const start = window.performance.now();
process();
const elapsed = window.performance.now() - start;
average += elapsed / n;
return i < n
? time_rec(process, n, average, i + 1)
: average;
};
それでは、次はいよいよ関数を入力にとって出力する関数の登場です。 そんなことして何が嬉しいのでしょうか??