在Unboxed Vectors上记忆函数会导致它挂起

时间:2014-07-02 11:38:50

标签: haskell

我正在使用一个相当简单的程序来玩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

2 个答案:

答案 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