我希望在生成新结果之前计算中间结果以获得memoization的好处。
import qualified Data.Map.Strict as M
import Data.List
parts' m = newmap
where
n = M.size m + 1
lists = nub $ map sort $
[n] : (concat $ map (\i -> map (i:) (M.findWithDefault [] (n-i) m)) [1..n])
newmap = seq lists (M.insert n lists m)
但是,如果我这样做
take 2000 (iterate parts' (M.fromList [(1,[[1]])]))
它仍然立即完成。
(可以使用数组代替地图帮助吗?)
答案 0 :(得分:4)
简答:
如果您需要一次计算整个列表/数组/地图/...,您可以使用deepseq
作为@JoshuaRahm建议,或使用($!!)
运算符
以下答案如何强制执行严格性,但仅限于第1级(它会进行评估,直到达到可能包含(余数)表达式树的数据结构)。
此外,答案认为为什么懒惰和记忆不是(必然)彼此对立。
更高级:
Haskell是一种懒惰的语言,它意味着只有计算的东西,如果绝对必要的话。表达式如下:
take 2000 (iterate parts' (M.fromList [(1,[[1]])]))
不会立即评估:Haskell只是存储这必须在以后计算。稍后,如果你真的需要第一个,第二个, i -th,或者列表的长度,它将评估它,甚至以一种懒惰的方式:如果你需要第一个元素,从它已经找到了计算该元素的方法,它将表示为:
element : take 1999 (<some-expression>)
然而,您可以强制Haskell使用exclamation mark (!
)严格评估某些内容,这称为 strictness 。例如:
main = do
return $! take 2000 (iterate parts' (M.fromList [(1,[[1]])]))
或者如果它是一个参数,你可以使用它:
f x !y !z = x+y+z
在此强制Haskell在&#34之前评估y
和z
;增加表达式树&#34;为:
表达换X
+
表达式换-γ+
表达式换-Z
编辑:如果您在let模式中使用它,可以use the bang as well:
let !foo = take 2000 (iterate parts' (M.fromList [(1,[[1]])])) in ...
请注意,您只将结构折叠到第一级。因此,let !foo
或多或少只会评估(_:_)
。
注意:请注意 memoization 和 lazyness 彼此不是必需的。考虑清单:
numbers :: [Integer]
numbers = 0:[i+(sum (genericTake i numbers))|i<-[1..]]
如您所见,计算数字需要大量的计算工作量。数字表示为:
numbers ---> (0:[i+(sum (genericTake i numbers))|i<-[1..]])
但是,如果我评估numbers!!1
,则必须计算第一个元素,它返回1
;但也会评估numbers
的内部结构。现在看起来像:
numbers (0:1:[i+(sum (genericTake i numbers))|i<-[2..]])
计算numbers!!1
因此将&#34;帮助&#34;未来的计算,因为你永远不必重新计算列表中的第二个元素。
如果您计算numbers!!4000
,则需要几秒钟。稍后如果你计算numbers!!4001
,它几乎会立即计算出来。仅仅因为numbers!!4000
已经完成的工作重用了。
答案 1 :(得分:4)
数组可能会有所帮助,但您也可以尝试利用deepseq库。所以你可以写这样的代码:
let x = take 2000 (iterate parts' (M.fromList [(1,[[1]])])) in do
x `deepseq` print (x !! 5) -- takes a *really* long time
print (x !! 1999) -- finishes instantly
答案 2 :(得分:4)
您正在记住分区功能,但您的方法存在一些缺点:
nub
和sort
以下是使用Data.Memocombinators
的方法:
import Data.Memocombinators
parts = integral go
where
go k | k <= 0 = [] -- for safety
go 1 = [[1]]
go n = [[n]] ++ [ (a : p) | a <- [n-1,n-2..1], p <- parts (n-a), a >= head p ]
E.g:
ghci> parts 4
[[4],[3,1],[2,2],[2,1,1],[1,1,1,1]]
此备忘录是动态的,因此只会记住您实际访问的值。
请注意它是如何构建的 - parts = integral go
和go
使用parts
进行任何递归调用。我们在这里使用integral
组合子,因为parts
是Int。