Два параметра memoization в Haskell
Я пытаюсь запомнить следующую функцию:
gridwalk x y
| x == 0 = 1
| y == 0 = 1
| otherwise = (gridwalk (x - 1) y) + (gridwalk x (y - 1))
смотрим этой Я придумал следующее решение:
gw :: (Int -> Int -> Int) -> Int -> Int -> Int
gw f x y
| x == 0 = 1
| y == 0 = 1
| otherwise = (f (x - 1) y) + (f x (y - 1))
gwlist :: [Int]
gwlist = map (i -> gw fastgw (i `mod` 20) (i `div` 20)) [0..]
fastgw :: Int -> Int -> Int
fastgw x y = gwlist !! (x + y * 20)
который я тогда могу назвать так:
gw fastgw 20 20
есть ли более простой, более краткий и общий способ (обратите внимание, как мне пришлось hardcode максимальные размеры сетки в gwlist
функция для преобразования из 2D в 1D пространство, чтобы я мог получить доступ к списку memoizing) для запоминания функций с несколькими параметрами в Хаскелл?
4 ответов
использовать данные-memocombinators пакет от hackage. Он обеспечивает простой в использовании методы запоминания и обеспечивает простой и breve способ их использования:
import Data.MemoCombinators (memo2,integral)
gridwalk = memo2 integral integral gridwalk' where
gridwalk' x y
| x == 0 = 1
| y == 0 = 1
| otherwise = (gridwalk (x - 1) y) + (gridwalk x (y - 1))
вы можете использовать список списков для запоминания результата функции для обоих параметров:
memo :: (Int -> Int -> a) -> [[a]]
memo f = map (\x -> map (f x) [0..]) [0..]
gw :: Int -> Int -> Int
gw 0 _ = 1
gw _ 0 = 1
gw x y = (fastgw (x - 1) y) + (fastgw x (y - 1))
gwstore :: [[Int]]
gwstore = memo gw
fastgw :: Int -> Int -> Int
fastgw x y = gwstore !! x !! y
вот версия, использующая Data.MemoTrie
чтобы запомнить функцию:
import Data.MemoTrie(memo2)
gridwalk :: Int -> Int -> Int
gridwalk = memo2 gw
where
gw 0 _ = 1
gw _ 0 = 1
gw x y = gridwalk (x - 1) y + gridwalk x (y - 1)
если вы хотите максимальную общность, вы можете запомнить функцию запоминания.
memo :: (Num a, Enum a) => (a -> b) -> [b]
memo f = map f (enumFrom 0)
gwvals = fmap memo (memo gw)
fastgw :: Int -> Int -> Int
fastgw x y = gwvals !! x !! y
этот метод будет работать с функции любое количество аргументов.
Edit: спасибо Филиппу к. за указание ошибки в исходном коде. Первоначально memo
имел" ограниченное "ограничение вместо" Num " и начал перечисление в minBound
, который будет действителен только для натуральных чисел.
списки не являются хорошей структурой данных для запоминания, хотя, потому что они имеют линейную сложность поиска. Возможно, Вам будет лучше с картой или IntMap. Или посмотреть on здесь.
обратите внимание, что этот конкретный код зависит от лени, поэтому, если вы хотите переключиться на использование карты, вам нужно будет взять ограниченное количество элементов из списка, как в:
gwByMap :: Int -> Int -> Int -> Int -> Int
gwByMap maxX maxY x y = fromMaybe (gw x y) $ M.lookup (x,y) memomap
where
memomap = M.fromList $ concat [[((x',y'),z) | (y',z) <- zip [0..maxY] ys]
| (x',ys) <- zip [0..maxX] gwvals]
fastgw2 :: Int -> Int -> Int
fastgw2 = gwByMap 20 20
Я думаю, что ghc может быть глупо делиться в этом случае, вам может потребоваться вытащить x
и y
параметры, вот так:
gwByMap maxX maxY = \x y -> fromMaybe (gw x y) $ M.lookup (x,y) memomap