関数型プログラミングでジェネレーター関数は有効ですか?


17

質問は次のとおりです。

  • ジェネレーターは関数型プログラミングのパラダイムを壊しますか?なぜですか?
  • はいの場合、ジェネレーターを関数型プログラミングで使用できますか?

以下を考慮してください。

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番目の要素が返されます。

より多くのコンテキストについては、これらの質問は、プログラミングカタの作業中に提起されました。質問に答えた人は、関数型プログラミングでジェネレーターを使用できるかどうか、およびジェネレーターが状態を保持するかどうかという質問を提起しました。


2
すべてのプログラムは状態を保持します。実際の問題は、それが機能状態として適格であるかどうかです。機能状態は、「不変状態」、つまり割り当てられた後は変化しない状態と解釈されます。ジェネレーターが各呼び出しで異なるものを返すようにする唯一の方法は、可変状態が何らかの形で関与している場合であると主張します。
ロバートハーヴェイ

回答:


14

ジェネレーター関数は特に特別ではありません。ジェネレーター関数をコールバックベースのスタイルで書き換えることにより、同様のメカニズムを実装できます。

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ます常にに評価する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などとは異なり、純粋な関数型言語ではありません。

ところで、Haskellでは、遅延評価を使用するため、リストとジェネレーターを返すのに違いはありません。

downCounter :: Int -> [Int]
downCounter maxValue =
  maxValue : (downCounter (max 0 (maxValue - 1)))
-- invoke as "take n (downCounter 26)" to display n elements
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.