純粋で決定的な関数

Justin Etheredgeの記事の翻訳。著者は、決定論的関数と純粋関数の微妙な違いを説明しています。



昨日、 Matt Podwysockiのブログを読んで(このブログは素晴らしいです、購読してください)、 「Recursing into Recursion-Memoization」という投稿があります。 メモ化について知りたい場合は、素晴らしい投稿をしてください。 私はすでにメモ化の一般化された機能についての投稿を少し前に持っていたので、メモ化については話しません。 私の興味をそそったのは、マットの記事の最後でした。



正しいメモ化には、作成する関数がクリーンである必要があることに注意してください。 つまり、同じ入力データに対して同じ結果が計算されて返されます。




私の最初の考えは、「ちょっとマット、純度は関数に副作用がないことを意味し、決定論は関数が与えられたパラメーターのセットに対して常に同じ結果を返すことを意味します」でした。 それから、他の優秀なプログラマーと同様に、私の第二の考えは「実際には、おそらく間違っている」ということでした。 それで、私はこの問題に行き、研究しました、そして、私が間違っていたのは驚くことではありません。





ウィキペディアのクリーン機能の説明は次のとおりです。

  1. この関数は、同じ引数のセットに対して常に同じ結果値を計算します。 関数の結果の値は、プログラムの実行中に変化する可能性のある隠された情報や状態に依存することはできず、 入出力デバイスからの外部入力に依存することはできません。
  2. 結果の計算によって、可変オブジェクトの突然変異や入力/出力デバイスへの出力など、意味的に観察可能な副作用が発生することはありません。



そして、ここに米国国立標準技術研究所(米国)による確定アルゴリズムの定義があります。



入力データから動作を完全に予測できるアルゴリズム。



したがって、純粋関数は実際には決定論的関数のサブセットです。 すべての純粋関数は決定論的ですが、すべての決定論関数が純粋というわけではありません。 たとえば、特定のパラメーターセットを取得して確定的な結果を返し、ディスクに値を書き込んだり、コンソールに出力したりする関数を作成できます。 定義上、そのような関数は決定的ですが、純粋ではありません。



例:



確定的でクリーン:



public int Add(int val1, int val2) { return val1 + val2; }
      
      







決定的だがクリーンではない:



 public int Add(int val1, int val2) { Console.WriteLine(val1 + " " + val2); return val1 + val2; }
      
      







確定的関数は、機能に影響するため、外部状態も使用できません。 ほとんどの確定関数定義は、入力から結果を決定できると言っています。



Mattが投稿で述べたように、メモ化には関数が決定論的であることが非常に重要です。 これは明らかです:多くの入力があり、その後多くの結果をキャッシュするという事実のために、この入力での各呼び出しの後に関数が同じ結果を返すことを期待します。そうでなければ、メモ化で関数の動作を変更します。



関数の純度は多くの理由で重要ですが、何よりもまず、関数の動作についてより簡単に話すことができるためです。 関数が純粋で、副作用がない場合、より少ない変数を考慮する必要があるため、この関数のパフォーマンスをより自信を持って議論できます。 また、その動作の複雑な分析なしでは、副作用を持つ関数を効果的に並列実行することはできません。 純粋な関数は、渡される値以外に依存してはなりません。 このような関数は分割状態を変更しないため、ロックを使用せずに並行して実行できます。



純粋な関数が何を表しているのか分からない場合は、良いアイデアをお持ちであることを願っています。 また、私のように誤解があれば、すべてが明確になっていることを願っています。



何か新しいことを学ぶことは本当に素晴らしいことですが、あなたの知識が間違っていたことを学ぶことはさらに良いことです。



All Articles