web-dev-qa-db-ja.com

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

質問は次のとおりです。

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

以下を検討してください。

_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 の作業中に発生しました。質問に答えた人は、ジェネレーターが関数型プログラミングで使用できるかどうか、そしてジェネレーターが状態を保持するかどうかという質問を提起しました。

17
Pete

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

_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().valuealwaysがどこで発生しても、また_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
_
16
amon