我求求你的帮助,加快以下计划:
main = do
jobsToProcess <- fmap read getLine
forM_ [1..jobsToProcess] $ \_ -> do
[r, k] <- fmap (map read . words) getLine :: IO [Int]
putStrLn $ doSomeReallyLongWorkingJob r k
可能(!)要做很多相同的工作,但是我不能修改输入,所以我尝试使用Data.HashMap
来备份已处理的作业。我已经在doSomeReallyLongWorkingJob
函数中优化了算法,但现在看来,它的速度和C一样快。
但遗憾的是,我似乎无法在不产生大量错误的情况下实现简单缓存。我需要一个类型HashMap (Int, Int) Int
的简单缓存,但每次我都有太多或太少的括号。如果我设法定义缓存,我就会陷入将数据放入或从缓存中检索数据导致大量错误。
我已经谷歌搜索了几个小时,但似乎我被卡住了。顺便说一句:longrunner
的结果也是Int
。
答案 0 :(得分:4)
进行缓存操作的有状态操作非常简单。首先是一些样板:
{-# LANGUAGE FlexibleContexts #-}
import Control.Monad.State
import Data.Map (Map)
import qualified Data.Map as M
import Debug.Trace
我会使用Data.Map
,但当然您可以在哈希映射或任何类似的数据结构中替换而不会有太多麻烦。我长时间运行的计算只会加上它的论点。我将使用trace
来显示执行此计算的时间;当我们输入重复输入时,我们希望不会看到trace
的输出。
reallyLongRunningComputation :: [Int] -> Int
reallyLongRunningComputation args = traceShow args $ sum args
现在,缓存操作只会查看我们之前是否看过某个输入。如果有,我们将返回预先计算的答案;否则我们现在就计算答案并存储它。
cache :: (MonadState (Map a b) m, Ord a) => (a -> b) -> a -> m b
cache f x = do
mCached <- gets (M.lookup x)
case mCached of
-- depending on your goals, you may wish to force `result` here
Nothing -> modify (M.insert x result) >> return result
Just cached -> return cached
where
result = f x
main
函数现在只需在适当的输入上调用cache reallyLongRunningComputation
。
main = do
iterations <- readLn
flip evalStateT M.empty . replicateM_ iterations
$ liftIO getLine
>>= liftIO . mapM readIO . words
>>= cache reallyLongRunningComputation
>>= liftIO . print
让我们在ghci中尝试一下!
> main
5
1 2 3
[1,2,3]
6
4 5
[4,5]
9
1 2
[1,2]
3
1 2
3
1 2 3
6
正如您在括号内输出中看到的那样,reallyLongRunningComputation
在我们第一次输入1 2 3
并且第一次输入1 2
时被调用,但不是第二次我们输入这些输入
答案 1 :(得分:1)
我希望我离基地不太远,但首先你需要一种方法来随身携带过去的工作。最简单的方法是使用foldM而不是forM。
import Control.Monad
import Data.Maybe
main = do
jobsToProcess <- fmap read getLine
foldM doJobAcc acc0 [1..jobsToProcess]
where
acc0 = --initial value of some type of accumulator, i.e. hash map
doJobAcc acc _ = do
[r, k] <- fmap (map read . words) getLine :: IO [Int]
case getFromHash acc (r,k) of
Nothing -> do
i <- doSomeReallyLongWorkingJob r k
return $ insertNew acc (r,k) i
Just i -> do
return acc
注意,我实际上并没有使用该接口来放置和获取哈希表键。它实际上不必是哈希表,容器中的Data.Map可以工作。或者甚至是一个列表,如果它是一个小的。
携带哈希表的另一种方法是使用状态转换器monad。
答案 2 :(得分:0)
我只是添加了这个答案,因为我觉得其他答案与原始问题略有不同,即在Main函数中使用hashtable结构(在IO monad中)。
这是使用hashtables模块的最小哈希表示例。要使用cabal安装模块,只需使用
cabal安装哈希表
在这个例子中,我们只是将一些值放在哈希表中,并使用lookup来打印从表中检索的值。
import qualified Data.HashTable.IO as H
main :: IO ()
main = do
t <- H.new :: IO (H.CuckooHashTable Int String)
H.insert t 22 "Hello world"
H.insert t 5 "No problem"
msg <- H.lookup t 5
print msg
请注意,我们需要使用显式类型注释来指定我们希望使用的哈希表的哪个实现。