質問は次のとおりです。
以下を検討してください。
_function * downCounter(maxValue) {
yield maxValue;
yield * downCounter(maxValue > 0 ? maxValue - 1 : 0);
}
let counter = downCounter(26);
counter.next().value; // 26
counter.next().value; // 25
// ...etc
_
downCounter
メソッドはステートレスに見えます。同様に、同じ入力でdownCounter
を呼び出すと、常に同じ出力になります。ただし、同時にnext()
を呼び出しても一貫した結果は得られません。
この例ではcounter
はジェネレーターオブジェクトであり、next()
を呼び出すと、同じmaxValue
。
同様に、配列で_someCollection[3]
_を呼び出すと、常に4番目の要素が返されます。同様に、ジェネレータオブジェクトでnext()
を4回呼び出すと、常に4番目の要素が返されます。
詳細については、これらの質問は programming kata の作業中に発生しました。質問に答えた人は、ジェネレーターが関数型プログラミングで使用できるかどうか、そしてジェネレーターが状態を保持するかどうかという質問を提起しました。
ジェネレータ関数は特に特別なものではありません。コールバックベースのスタイルでジェネレーター関数を書き換えることにより、同様のメカニズムを自分で実装できます。
_function downCounter(maxValue) {
return {
"value": maxValue,
"next": function () {
return downCounter(maxValue > 0 ? maxValue - 1 : 0);
},
};
}
let counter = downCounter(26);
counter.value; //=> 26
counter.next().value; //=> 25
_
明らかに、downCounter
は、純粋で機能的です。ここで問題はありません。
JavaScriptで使用されるジェネレータプロトコルには、可変オブジェクトが含まれます。これは必要ありません。上記のコードを参照してください。特に、可変オブジェクトは、式をその値で置き換える機能参照の透明度 –を失うことを意味します。私の例では、counter.next().value
はalwaysがどこで発生しても、また_25
_に評価されますが、JSジェネレーターには当てはまりません–ある時点では、それは_26
_であり、次に_25
_であり、実際には任意の数になる可能性があります。ジェネレータへの参照を別の関数に渡す場合、これは問題になります。
_counter.next().value; //=> 25
otherFunction(counter); // does this consume the counter?
counter.next().value; // what will this be? It depends on the otherFunction()
_
明らかに、ジェネレーターは状態を保持するため、「純粋な」関数型プログラミングには適していません。幸い、純粋な関数型プログラミングを行う必要はなく、代わりに実用的である場合があります。ジェネレーターによってコードがより明確になる場合は、良心を損なわずに使用する必要があります。結局のところ、JavaScriptは純粋な関数型言語ではありません。ハスケル。
ちなみに、Haskellでは遅延評価を使用しているため、リストを返す場合とジェネレータを返す場合に違いはありません。
_downCounter :: Int -> [Int]
downCounter maxValue =
maxValue : (downCounter (max 0 (maxValue - 1)))
-- invoke as "take n (downCounter 26)" to display n elements
_