Haskell中的非单片阵列

时间:2012-04-19 10:37:38

标签: arrays haskell

我接受了下面问题的答案,但似乎我误解了haskell中的阵列是如何工作的。我以为他们只是加强了名单。阅读以下问题时请记住这一点。


我发现haskell中的单片阵列在将它们用于更大的阵列时效率很低。

我无法在haskell中找到非单片的数组实现。我需要的是O(1)时间查找多维数组。

是否有支持此功能的数组实现?

编辑:我似乎误解了整体一词。问题是似乎haskell中的数组像列表一样处理数组。我可能错了。

EDIT2:低效代码的简短示例:

fibArray n = a where
  bnds = (0,n)
  a = array bnds [ (i, f i) | i <- range bnds ]
  f 0 = 0
  f 1 = 1
  f i = a!(i-1) + a!(i-2)

这是一个长度为n+1的数组,其中第i个字段包含第i个斐波纳契数。但由于haskell中的数组具有O(n)时间查找,因此计算需要O(n²)时间。

3 个答案:

答案 0 :(得分:8)

你把Haskell中的链表与数组混淆了。

链接列表是使用以下语法的数据类型:

[1,2,3,5]

定义为:

data [a] = [] | a : [a]

这些是经典的递归数据类型,支持O(n)索引和O(1)前置。

如果您正在寻找使用O(1)查找的多维数据,则应使用真正的数组或矩阵数据结构。好的候选人是:

  • Repa - 快速,并行,多维数组 - (Tutorial
  • Vector - Int-indexed数组(可变和不可变)的有效实现,具有强大的循环优化框架。 (Tutorial
  • HMatrix - 基本线性代数和其他数值计算的纯函数接口,内部使用GSL,BLAS和LAPACK实现。

答案 1 :(得分:5)

数组有O(1)索引。问题是每个元素都是懒惰计算的。所以当你在ghci中运行它时会发生这种情况:

*Main> :set +s
*Main> let t = 100000
(0.00 secs, 556576 bytes)
*Main> let a = fibArray t
Loading package array-0.4.0.0 ... linking ... done.
(0.01 secs, 1033640 bytes)
*Main> a!t  -- result omitted
(1.51 secs, 570473504 bytes)
*Main> a!t  -- result omitted
(0.17 secs, 17954296 bytes)
*Main> 

请注意,查找非常快, 之后已经查找过一次。 array函数创建一个指向thunk的指针数组,最终将计算这些指针以生成值。第一次评估价值时,您需要支付此费用。以下是用于评估a!t的thunk的前几个扩展:

a!t -> a!(t-1)+a!(t-2)-> a!(t-2)+a!(t-3)+a!(t-2) -> a!(t-3)+a!(t-4)+a!(t-3)+a!(t-2)

计算本身的成本并不昂贵,而是需要创建和遍历这个非常大的thunk。

我尝试严格传递给array的列表中的值,但这似乎导致无限循环。

一种常见的方法是使用可变数组,例如STArray。元素可以在阵列创建期间更新,并且最终结果被冻结并返回。在向量包中,createconstructN函数提供了简单的方法。

-- constructN :: Unbox a => Int -> (Vector a -> a) -> Vector a


import qualified Data.Vector.Unboxed as V
import Data.Int

fibVec :: Int -> V.Vector Int64
fibVec n = V.constructN (n+1) c
 where
  c v | V.length v == 0 = 0 
  c v | V.length v == 1 = 1 
  c v | V.length v == 2 = 1
  c v = let len = V.length v
        in v V.! (len-1) + v V.! (len-2)

但是fibVec功能仅适用于未装箱的矢量。常规向量(和数组)不够严格,导致回到您已经发现的同样问题。不幸的是,Integer没有一个Unboxed实例,所以如果你需要无界的整数类型(这个fibVec已经在这个测试中已经溢出)你就会在{{1}中创建一个可变数组}或IO以实现必要的严格性。

答案 2 :(得分:1)

具体参考您的fibArray示例,请尝试此操作,看看它是否会加快速度:

-- gradually calculate m-th item in steps of k
--     to prevent STACK OVERFLOW , etc
gradualth m k arr                         
    | m <= v = pre `seq` arr!m   
  where                                   
    pre = foldl1 (\a b-> a `seq` arr!b) [u,u+k..m]
    (u,v) = bounds arr 

对我来说,对于let a=fibArray 50000gradualth 50000 10 a在0.65运行时运行,只需立即调用a!50000