我正在使用一个相当简单的程序来玩Vector和Unboxed Vectors。我列举了所有只有因子为2,3&的整数。 5。
我以为我会尝试通过Data.Vector
进行备忘,但这非常容易。所以我想我会尝试Data.Vector.Unboxed
。
但是,当z为[0..5]
时,它会挂起,但当z为[0..4]
时则不会挂起,我不确定原因。
两者之间的区别在于5
涉及相互递归的调用。
这里出了什么问题?
import Data.Vector.Unboxed as UV
memoisingCandidateUV :: UV.Vector Bool
memoisingCandidateUV = UV.map isCandidateUV z
isCandidateUV :: Int -> Bool
isCandidateUV 0 = False
isCandidateUV n
| n2r == 0 = n2q == 1 || memoisingCandidateUV UV.! n2q
| n3r == 0 = n3q == 1 || memoisingCandidateUV UV.! n3q
| n5r == 0 = n5q == 1 || memoisingCandidateUV UV.! n5q
| otherwise = False
where
(n2q, n2r) = n `quotRem` 2
(n3q, n3r) = n `quotRem` 3
(n5q, n5r) = n `quotRem` 5
答案 0 :(得分:8)
所有未装箱的容器都是深度严格的,这意味着你不能将它们用于懒惰的记忆:在任何元素可以被记忆后访问之前,你必须所有这些> 评估。
至于为什么会这样:当你在Haskell中有一个正常的懒惰时,它就是框带有关于那个的信息,这个值现在还没有在NF中,但是使它成为可以执行这段代码。完成之后,框基本上只是指向结果的指针。 在许多情况下,这是一个很好的帮助,但它不是免费的内存或性能:这正是未装箱的容器删除的开销,通过直接将结果信息存储在一个紧密的数组中
答案 1 :(得分:6)
在这种特定情况下(所有递归调用都是较小的索引,n
可以从已经计算的向量重建),你可以使用constructN(它迭代地工作而不是依赖于懒惰):
import Data.Vector.Unboxed as UV
memoisingCandidateUV :: UV.Vector Bool
memoisingCandidateUV = UV.constructN 100 isCandidateUV
isCandidateUV :: UV.Vector Bool -> Bool
isCandidateUV vec = result
where
n = UV.length vec
result
| n == 0 = False
| n2r == 0 = n2q == 1 || vec UV.! n2q
| n3r == 0 = n3q == 1 || vec UV.! n3q
| n5r == 0 = n5q == 1 || vec UV.! n5q
| otherwise = False
(n2q, n2r) = n `quotRem` 2
(n3q, n3r) = n `quotRem` 3
(n5q, n5r) = n `quotRem` 5