rgrinberg が提供するソリューションは一般化できるため、任意の関数をメモ化できます。ハッシュテーブルの代わりに連想リストを使用します。しかし、それは実際には問題ではありません。すべての例をハッシュテーブルを使用するように簡単に変換できます。
まず、memo別の関数を受け取り、メモ化されたバージョンを返す関数を次に示します。これは、nlucaroni がコメントの 1 つで提案したものです。
let memo f =
let m = ref [] in
fun x ->
try
List.assoc x !m
with
Not_found ->
let y = f x in
m := (x, y) :: !m ;
y
この関数は、これまでに計算された結果memo fのリストを保持します。m計算を求められるf xと、まずが既に計算されているかどうかを確認mします。f xはいの場合は結果を返します。そうでない場合は、実際に を計算f xし、結果を に格納してm返します。
が再帰的memoである場合、上記には問題があります。f一度computeをmemo呼び出すと、 によって行われた再帰呼び出しは によってインターセプトされません。この問題を解決するには、次の 2 つのことを行う必要があります。ff xfmemo
このような再帰の定義では、f再帰呼び出しを「後で提供される」関数の呼び出しに置き換える必要があります (これは のメモ化されたバージョンになりますf)。
では、約束された「再帰呼び出しを行いたいときに呼び出す関数」memo fを提供する必要があります。f
これにより、次の解決策が導き出されます。
let memo_rec f =
let m = ref [] in
let rec g x =
try
List.assoc x !m
with
Not_found ->
let y = f g x in
m := (x, y) :: !m ;
y
in
g
これがどのように機能するかを示すために、単純なフィボナッチ関数をメモしてみましょう。追加の引数を受け入れるように記述する必要があります。これを と呼びますself。この引数は、関数が自分自身を再帰的に呼び出す代わりに使用するものです。
let fib self = function
0 -> 1
| 1 -> 1
| n -> self (n - 1) + self (n - 2)
メモ化された を取得するために、fib計算します
let fib_memoized = memo_rec fib
fib_memoized 50すぐに元に戻るので、ぜひお試しください。(これは、通常の素朴な再帰的定義memo fである where には当てはまりません。)f