我正在练习我的Haskell,我遇到了一个奇怪的问题,我无法在互联网上找到解决方案。我决定解决这个问题:
https://www.hackerrank.com/challenges/fibonacci-fp
在很多方面我都能想到。一种方法是使用memoization执行递归,其中我想使用State monad作为缓存。我的Windows 10上有GHC 7.10.2,Ubuntu 14.04上有GHC 7.6.2。下面的代码在7.6.2上编译(并且运行得非常好),并且不会在7.10.2上编译,只要我键入地图' Map'就会给出错误,例如:
不在范围内:类型构造函数或类:' Map.Map'
不在范围内:' Map.lookup'
module Main (
main
) where
import qualified Data.Map as Map
import Control.Monad.State
type CacheState = Map.Map Int Int
type IOState a = StateT CacheState IO a
modNum :: Int
modNum = 100000007
fibsMod :: [Int]
fibsMod = 0 : 1 : zipWith (\x y -> (x + y) mod modNum ) fibsMod (tail fibsMod)
-- | calculate Fibs with memoization in map
memoizedFib :: Int -> IOState Int
memoizedFib n = do
state <- get
let x = Map.lookup n state
case x of
Just y ->
return y
Nothing -> do
n1 <- memoizedFib (n - 1)
n2 <- memoizedFib (n - 2)
let n3 = mod (n1 + n2) modNum
put (Map.insert n n3 state)
return n3
query :: [Int] -> IOState ()
query [] = return ()
query (n:ns) = do
fibNum <- memoizedFib n
liftIO $ print fibNum
query ns
main :: IO ()
main = do
inputdata <- getContents
let intList = (map (read :: String -> Int) . tail . words) inputdata
evalIOState $ query intList
where
initState :: Int -> Map.Map Int Int
initState upTo = Map.fromList $ zip [0 .. upTo] $ take upTo fibsMod
--initState upTo = Map.fromList $ [(0, 0), (1, 1)]
evalIOState :: IOState a -> IO a
evalIOState m = evalStateT m (initState 10001)
有人知道为什么我会遇到这个问题吗?这非常令人不安。
其他问题 正如您所看到的,我没有使用memoization执行完全递归。但是,如果将这些行中的一行取消注释,可以改变方法:
initState upTo = Map.fromList $ zip [0 .. upTo] $ take upTo fibsMod
--initState upTo = Map.fromList $ [(0, 0), (1, 1)]
问题是使用第二行表现糟糕。我不知道我犯了什么错误,但我认为它应该在线性时间内进行记忆。然而,对于这条线,我的算法显然是指数级的(我甚至无法获得第50个Fib数的答案 - 那么长)。在这种情况下我做错了什么?
更新
感谢您的评论,我修复了我的代码。显然mod
函数存在问题(我完全不知道这是如何在GHC 7.6.2上编译的)。我也改变了:
import qualified Data.Map as Map
为:
import qualified Data.Map.Strict as Map
现在这个代码按预期工作:
module Main (
main
) where
import qualified Data.Map.Strict as Map
import Control.Monad.State
type CacheState = Map.Map Int Int
type IOState a = StateT CacheState IO a
modNum :: Int
modNum = 100000007
fibsMod :: [Int]
fibsMod = 0 : 1 : zipWith (\x y -> (x + y) `mod` modNum) fibsMod (tail fibsMod)
-- | calculate Fibs with memoization in map
memoizedFib :: Int -> IOState Int
memoizedFib n = do
state <- get
let x = Map.lookup n state
case x of
Just y ->
return y
Nothing -> do
n1 <- memoizedFib (n - 1)
n2 <- memoizedFib (n - 2)
state <- get
let n3 = mod (n1 + n2) modNum
put (Map.insert n n3 state)
return n3
query :: [Int] -> IOState ()
query [] = return ()
query (n:ns) = do
fibNum <- memoizedFib n
liftIO $ print fibNum
query ns
main :: IO ()
main = do
inputdata <- getContents
let intList = (map (read :: String -> Int) . tail . words) inputdata
evalIOState $ query intList
where
initState :: Int -> Map.Map Int Int
--initState upTo = Map.fromList $ zip [0 .. upTo] $ take upTo fibsMod
initState upTo = Map.fromList [(0, 0), (1, 1)]
evalIOState :: IOState a -> IO a
evalIOState m = evalStateT m (initState 10001)
所以现在问题归结为:为什么我需要使用Data.Map.Strict,它有何不同以及GHC 7.6.2为什么不需要它?