Ну, есть Data.HashTable
. Хеш-таблицы не всегда хорошо играют с неизменяемыми данными и ссылочной прозрачностью, поэтому я не думаю, что они могут найти широкое применение.
Для небольшого количества значений сохранение их в дереве поиска (например, Data.Map
), вероятно, будет достаточно быстрым. Если вы можете смириться с каким-либо искажением ваших Double
s, более надежным решением было бы использование трип-подобной структуры, такой как Data.IntMap
; они имеют время поиска, пропорциональное в первую очередь длине ключа, и примерно постоянное в размере коллекции. Если Int
слишком ограничен, вы можете покопаться в Hackage, чтобы найти три библиотеки, более гибкие в типе используемого ключа.
Что касается того, как кешировать результаты, я думаю, что то, что вы хотите, обычно называется "памятка" . Если вы хотите вычислять и запоминать результаты по требованию, суть метода состоит в том, чтобы определить индексированную структуру данных, содержащую все возможные результаты , таким образом, чтобы при запросе определенного результата он вызывал только вычисления, необходимые, чтобы получить ответ, который вы хотите. Распространенные примеры обычно включают в себя индексирование в список, но тот же принцип должен применяться к любой нестрогой структуре данных. Как правило, нефункциональные значения (включая бесконечные рекурсивные структуры данных) часто будут кэшироваться во время выполнения, но не в результатах функций, поэтому хитрость заключается в том, чтобы обернуть все ваши вычисления в определение верхнего уровня, которое не зависит от любых аргументов.
Редактировать: Пример MemoTrie, привет!
Это быстрое и грязное доказательство концепции; лучшие подходы могут существовать.
{-# 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
Обратите внимание на расширения GHC, используемые пакетом MemoTrie; надеюсь, это не проблема. Загрузите его в GHCi и попробуйте вызвать slow
против memoSlow
с чем-то вроде (10 ^ 6) или (10 ^ 7), чтобы увидеть его в действии.
Обобщение этого для функций, принимающих несколько аргументов или еще много чего, должно быть довольно простым. Для получения более подробной информации об использовании MemoTrie вы можете найти это сообщение в блоге его автора полезным.