メモ化された純粋な関数自体は純粋と見なされますか?


47

たとえばfn(x)、の素因数のリストを返すなど、高価な処理を行う純粋な関数だとしますx

そして、と呼ばれる同じ関数のメモバージョンを作成するとしmemoizedFn(x)ます。特定の入力に対して常に同じ結果を返しますが、パフォーマンスを向上させるために以前の結果のプライベートキャッシュを維持します。

正式に言えば、memoizedFn(x)純粋と見なされますか?

または、FPディスカッションでそのような関数を参照するために使用される他の名前または修飾用語はありますか?(つまり、後続の呼び出しの計算の複雑さに影響する可能性があるが、戻り値には影響しない可能性がある副作用を持つ関数。


24
純粋主義者にとっては純粋ではないかもしれませんが、実用的な人々にとっては「十分に純粋」です;-)
Doc Brown

2
@DocBrown「純粋な」というより正式な用語があるのではないかと思って同意します
コラム

13
純粋な関数を実行すると、プロセッサの命令キャッシュ、分岐予測子などが変更される可能性が高くなります。しかし、それはおそらく純粋主義者にとっても「十分に純粋」です。
gnasher729

10
@callumいいえ、「純粋な」という正式な定義はありません。2つの「参照透過的」呼び出しの純度とセマンティクスの等価性について議論する場合、適用するセマンティクスを常に正確に述べる必要があります。実装の詳細レベルが低い場合、常に故障し、異なるメモリ効果またはタイミングが発生します。それがあなたが実用的でなければならない理由です:あなたのコードについて推論するのにどのレベルの詳細が有用ですか?
ベルギ

3
次に、プラグマティズムのために、純度は計算時間を出力の一部とみなすかどうかに依存すると言います。funcx(){sleep(cached_time--); return 0;}同じvalのたびに返されますが、別々に実行されます
火星

回答:


41

はい。純関数のメモ化バージョンも純関数です。

関数の純度が重視するのは、関数の戻り値の入力パラメーター(同じ入力を渡すと常に同じ出力が生成される)と、グローバル状態に関連する副作用(たとえば、端末またはUIまたはネットワークへのテキスト)の影響です。計算時間と余分なメモリ使用量は、関数の純度とは無関係です。

純粋な関数のキャッシュは、プログラムにはほとんど見えません。関数型プログラミング言語は、それが有益であると判断できる場合、純粋な関数をメモされた関数のバージョンに自動的に最適化できます。実際には、メモ化がいつ有益かを自動的に判断することは実際には非常に難しい問題ですが、そのような最適化は有効です。


19

ウィキペディアでは、「純粋な関数」を次のプロパティを持つ関数として定義しています。

  • その戻り値は、同じ引数に対して同じです(ローカルの静的変数、非ローカル変数、可変参照引数、またはI / Oデバイスからの入力ストリームによる変動はありません)。

  • その評価には副作用はありません(ローカルスタティック変数、非ローカル変数、可変参照引数またはI / Oストリームの変更はありません)。

実際には、純粋な関数は同じ入力が与えられると同じ出力を返しますが、関数以外の要素には影響しません。 純粋さを目的として、同じ入力が与えられたときに同じ出力を返す限り、関数がどのように戻り値を計算するかは関係ありません。

Haskellのような機能的に純粋な言語は、メモ化定期的に使用して、以前に計算された結果をキャッシュすることで機能を高速します。


16
私は何かを見逃すかもしれませんが、どのように副作用なしでキャッシュを保持するつもりですか?
val

1
関数内に保持することにより。
ロバートハーベイ

4
「ローカル静的変数の突然変異なし」は、呼び出し間で持続するローカル変数も除外するようです。
val

3
これが実際に質問に答えているわけではありません。たとえそれが「はい」であることを暗示しているように見えても、それは純粋です。
火星

6
@val正解です。この状態は少し緩和する必要があります。彼が言及する純粋に機能的なメモ化には、静的データの目に見える変異はありません。起こることは、関数が最初に呼び出されたときに結果が計算されてメモされ、呼び出されるたびに同じ値を返すことです。多くの言語にはそのためのイディオムがありstatic constます:C ++のローカル変数(Cではない)、またはHaskellの遅延評価されたデータ構造。必要な条件がもう1つあります。初期化はスレッドセーフでなければなりません。
デイビスラー

7

はい、メモされた純粋な関数は一般的に純粋と呼ばれます。これは特に、Haskellなどの言語で一般的です。Haskellでは、メモされ、遅延評価され、不変の結果が組み込まれています。

重要な注意事項が1つあります。メモ化関数はスレッドセーフでなければなりません。そうしないと、2つのスレッドの両方がそれを呼び出そうとすると競合状態になる可能性があります。

このように「純粋に機能的」という用語を使用するコンピューター科学者の一例は、自動メモ化に関するConal Elliottの次のブログ投稿です。

おそらく驚くべきことに、メモ化は怠andな関数型言語で簡単かつ純粋に機能的に実装できます。

査読済みの文献には多くの例があり、数十年前から存在しています。たとえば、1995年の「Real-World AI Systemsのソフトウェアエンジニアリングツールとしての自動メモ化の使用」のこのペーパーでは、セクション5.2で非常によく似た言語を使用して今日純粋関数と呼ぶものを説明しています。

メモ化は、プロシージャではなく、真の関数に対してのみ機能します。つまり、関数の結果がその入力パラメーターによって完全かつ決定論的に指定されていない場合、メモ化を使用すると誤った結果が得られます。正常にメモできる機能の数は、システム全体で関数型プログラミングスタイルの使用を奨励することにより増加します。

一部の命令型言語には、同様のイディオムがあります。たとえばstatic const、C ++の変数は、その値が使用される前に一度だけ初期化され、変更されることはありません。


3

それはあなたのやり方次第です。

通常、人々はある種のキャッシュディクショナリを変更することでメモしたいと考えています。これには、並行性を心配する必要がある、キャッシュが大きくなりすぎることを心配するなど、不純な突然変異に関連するすべての問題があります。

ただし、不純な記憶の突然変異なしでメモすることができます。1つの例はこの回答にありlengthsます。ここでは、引数を使用して、メモされた値を外部から追跡しています。

ではロバート・ハーヴェイは、提供されたリンク、遅延評価は副作用を回避するために使用されます。

時々見られる別のテクニックはIO、cats-effectのmemoize functionなどのように、型のコンテキストでメモ化を不純な副作用として明示的にマークすることです。

この最後の1つは、突然変異を排除するのではなく、単にカプセル化することを目標とする場合があることを指摘しています。ほとんどの関数型プログラマーは、不純物を明示的にカプセル化するのに「十分に純粋」だと考えています。

用語を本当に純粋な関数と区別したい場合は、「可変辞書でメモした」と言うだけで十分だと思います。それにより、人々はそれを安全に使用する方法を知ることができます。


より純粋な解決策は上記の問題を解決しないと思います。並行性の心配はなくなりますが、同時に開始された2つのコールのようcollatz(100)collatz(200)、協力する機会も失われます。また、IIUICでは、キャッシュが大きくなりすぎるという問題が残ります(ただし、Haskellにはこのためのいいトリックがあるかもしれません)。
maaartinus

注:IOは純粋です。IOおよびCats 上のすべての不純なメソッドには名前が付けられunsafeます。Async.memoize純粋でもあるので、「十分に純粋」に落ち着く必要はありません:)
Samuel

2

通常、リストを返す関数は、ストレージの割り当てを必要とし、それによって失敗する可能性があるため(純粋でない例外をスローするなど)、まったく純粋ではありません。値型を持ち、リストを制限サイズの値型として表すことができる言語には、この問題がない場合があります。このため、おそらくあなたの例は純粋ではありません。

一般に、失敗が発生しない方法でメモ化を行うことができる場合(たとえば、メモ化された結果に静的に割り当てられた記憶域、および言語がスレッドを許可する場合にそれらへのアクセスを制御する内部同期を使用することにより)、そのような関数を検討するのが妥当です純粋。


0

状態モナドを使用して、副作用のないメモ化を実装できます。

[State monad]は基本的に関数S =>(S、A)です。Sは状態を表す型で、Aは関数が生成する結果-Cats Stateです。

あなたの場合、状態はメモされた値または何もない(つまりHaskell MaybeまたはScala Option[A])でしょう。メモ化された値が存在する場合、それはとして返されますA。それ以外の場合A、計算され、遷移状態と結果の両方として返されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.