Два параметра 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