JavaScript で遅延評価をやってみる
遅延評価とは
遅延評価は必要になった時に計算をすることです。
JavaScriptでは値ではなく関数を使用することでこれを実現できます。
シンプルな例
具体例をみます。
const add = (x, y) => x + y;
add(3, 4); //7
add は二つの引数を足した値を返すシンプルな関数です。
では次のように引数を渡した場合、計算の順番はどのようになるでしょうか。
add(1+2, 2+2); //7
以下のような流れになります。
- 引数の一つ目の1+2が計算され3が算出される
- 引数の二つ目の2+2が計算され4が算出される
- 関数内の処理に移り、3+4が実行されて7が算出される
次にadd関数を少し変更します。
const add = (flag, x, y) => {
if(flag) return x + y;
return 0;
};
第一引数がtrueの場合はx+yを計算した結果を返し、falseの場合は0を返します。
add(true, 1+2, 2+2); //7
add(false, 1+2, 2+2); //0
よく考えてみると、false の場合は1+2や2+2の処理はやらなくも問題ありません。
むしろ、1+2や2+2の処理がもっと複雑で時間がかかる処理の場合(例えば大きいファイルからデータを読み込むなど)、無駄な処理を行っていることになります。
ここで登場するのが遅延評価です。
遅延評価を使って、第一引数がtrueのときのみ引数の計算を実行するようにしてみます。
冒頭で書いたようにJavaScriptで遅延評価を行う際には、値ではなく関数を引数に渡します。
以下はこれまでのaddと同じ結果になります。
const add = (flag, x, y) => {
if(flag) return x() + y();
return 0;
};
add(true,() => 1+2, () => 2+2); //7
先ほどより複雑になったように見えますが、ひとつひとつ見ていきます。
add関数の引数の記述は特に変わっていませんが、xとyは関数を想定しています。
そして、flagがtrueの時は、それぞれの関数の実行結果を足しています。
const add = (flag, x, y) => {
if(flag) return x() + y(); //xという関数を実行した結果と、yという関数を実行した結果を足している
return 0;
};
JavaScriptでは関数を値として引数に渡せるために、このようなことができます。
addを使う時は、第二引数と第3引数には関数を渡します。
add(true,() => 1+2, () => 2+2);
もう少し分解すると、以下のようになっているのと同じです。
const xFunc = () => 1+2; //1+2の結果を返す関数
const yFunc = () => 2+2; //2+2の結果を返す関数
add(true, xFunc, yFunc);
このように引数に関数を渡すことで、flagがtrueの時のみ、1+2や2+2の処理が行われるので、無駄な計算を避けることができます。
具体的なサンプル
先ほどと同じように、add関数は第一引数がtrueなら第二引数と第三引数の合計値を返し、そうでない場合は0を返すとします。
const add = (flag, x, y) => {
if(flag) return x + y;
return 0;
};
xとyには、すべての要素の値が1で長さが99999の配列の、すべての要素を合計した値を渡すとします。
配列を作成する処理と、配列の全要素を合計する処理を作成しておきます。
//第一引数の長さの配列を作成する。要素の値はすべて1
const createArray = (length) => Array(length).fill(1);
//配列のすべての要素を合計した値を返す
const sum = array => array.reduce((acc, value) => {
console.log('current value', value);
return acc + value;
}, 0);
まず遅延評価を使わない場合です。
const createArray = (length) => Array(length).fill(1);
const sum = array => array.reduce((acc, value) => {
console.log('current value', value);
return acc + value;
}, 0);
const add = (flag, x, y) => {
if(flag) return x + y;
return 0;
};
const myArray1 = createArray(99999);
const myArray2 = createArray(77777);
const result = add(false, sum(myArray1), sum(myArray2));
console.log(result);
add関数の第一引数はfalseですが、実行してみると「current value 1」というログが大量にでます。
これはsum関数内の処理が動いていることを示しています。つまり無駄な計算が動いています。
言い換えると、第一引数がtrueかfalseかにかかわらず、第二引数、第三引数のsum(myArray1) や sum(myArray2) の処理が実行されてしまっています。
次に遅延評価の場合です。addの第二引数と第三引数には関数を渡すようにします。
const createArray = (length) => Array(length).fill(1);
const sum = array => array.reduce((acc, value) => {
console.log('current value', value);
return acc + value;
}, 0);
const add = (flag, x, y) => {
if(flag) return x() + y(); //変更箇所 xとyは関数
return 0;
};
const myArray1 = createArray(99999);
const myArray2 = createArray(99999);
const result = add(false, () => sum(myArray1), () => sum(myArray2)); //変更箇所
console.log(result);
addの第一引数がfalseの場合は先ほどの「current value 1」というログが出力されない、つまり、無駄な計算が行われていないことをわかります。
まとめ
遅延評価を使うことで無駄な処理を避けることができる。
JavaScriptで遅延評価を行う場合は引数に値を渡すのではなく関数を渡す。
最近のコメント