さて、ありData.HashTable
ます。ただし、ハッシュテーブルは、不変のデータや参照透過性とうまく連携しない傾向があるため、あまり使用されていないと思います。
値の数が少ない場合は、検索ツリー(などData.Map
)にそれらを格納するのがおそらく十分に高速です。のマングリングを我慢できる場合Double
、より堅牢な解決策は、次のようなトライのような構造を使用することData.IntMap
です。これらのルックアップ時間は主にキーの長さに比例し、コレクションサイズはほぼ一定です。制限が大きすぎる場合Int
は、Hackageを調べて、使用するキーのタイプがより柔軟なトライライブラリを見つけることができます。
結果をキャッシュする方法については、通常「メモ化」と呼ばれるものが欲しいと思います。結果をオンデマンドで計算してメモ化したい場合、テクニックの要点は、特定の結果を要求したときに答えを得るのに必要な計算のみを強制するように、すべての可能な結果を含むインデックス付きデータ構造を定義することです。あなたが欲しい。一般的な例には通常、リストへのインデックス作成が含まれますが、厳密でないデータ構造にも同じ原則を適用する必要があります。経験則として、非関数値(無限の再帰データ構造を含む)はランタイムによってキャッシュされることがよくありますが、関数の結果はキャッシュされないため、トリックはすべての計算を、ラップしないトップレベルの定義内にラップすることです。引数に依存します。
編集: MemoTrieの例ahoy!
これは、迅速で汚い概念実証です。より良いアプローチが存在する可能性があります。
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
import Data.MemoTrie
import Data.Binary
import Data.ByteString.Lazy hiding (map)
mangle :: Double -> [Int]
mangle = map fromIntegral . unpack . encode
unmangle :: [Int] -> Double
unmangle = decode . pack . map fromIntegral
instance HasTrie Double where
data Double :->: a = DoubleTrie ([Int] :->: a)
trie f = DoubleTrie $ trie $ f . unmangle
untrie (DoubleTrie t) = untrie t . mangle
slow x
| x < 1 = 1
| otherwise = slow (x / 2) + slow (x / 3)
memoSlow :: Double -> Integer
memoSlow = memo slow
MemoTrieパッケージで使用されるGHC拡張機能に注意してください。うまくいけば、それは問題ではありません。それをGHCiにロードし、(10 ^ 6)や(10 ^ 7)のようなものでslow
vs.を呼び出してみて、動作を確認してください。memoSlow
これを複数の引数を取る関数に一般化するなど、かなり簡単なはずです。MemoTrieの使用の詳細については、作成者によるこのブログ投稿が役立つ場合があります。