【问题标题】:Appropriate representation for lazy array in HaskellHaskell中惰性数组的适当表示
【发布时间】:2017-04-18 07:47:46
【问题描述】:

我正在尝试制作一个程序,该程序在一个大型阵列上进行大量更新,但只评估几次。我希望计算尽可能地懒惰,但我找不到哪种数组表示适合我的情况。 具体来说,我希望我的数组:

  • 有固定大小
  • 在恒定时间内访问
  • 在 O(n) 中更新 n 个元素
  • 懒惰评估

如何满足这些要求?作为一个子问题:是否有专门针对此用例的库?


编辑:

也许我的问题不够具体,所以我会尝试更多地解释我的情况。

我试图表示各种尺寸的图像,它们可以相对较小(大约 1200x800),也可以比它大得多(至少 8000x8000)。此外,一个图像会有很多层,这意味着如果我想在屏幕上绘制图像,帧缓冲图像会有很多更新。我想如果我可以利用 haskell 的惰性求值特性,我将能够只在帧缓冲区上写入一次,而不是在每次更新时覆盖相同的像素。

我知道在 haskell 中表示数组的几个选项,但所有这些似乎都不适合我的情况。例如:

  • Data.Seq、Data.IntTrie : 无法在固定时间内访问
  • Data.Vector, Data.Array : 更新 n 个元素花费的时间超过 O(n)
  • 未装箱的变体:未进行惰性评估(我猜?)

在这种情况下我应该采取什么方法?

【问题讨论】:

  • 这种问题离题了(虽然这是个好问题,但我也经常对此感到疑惑)。 Seq 的目标当然是正确的,也许您可​​以改变您的问题,询问该特定类型是否满足您的要求。
  • @leftaroundabout 对此感到抱歉,但你能告诉我为什么这是题外话吗?
  • @leftaroundabout 我已经编辑了这个问题,试图淡化推荐方面。 (@Yang:引用相关的问题关闭原因:“要求我们推荐或查找书籍、工具、软件库、教程或其他非现场资源的问题对于 Stack Overflow 来说是无关紧要的,因为它们往往会吸引固执己见的答案和垃圾邮件. 相反,请描述问题以及迄今为止为解决该问题所做的工作。")
  • “但只评估几次”/“懒惰评估” - 我不明白你的要求。
  • @Yang Repa 有一个delayed 表示,它允许您操作“延迟”数组,其中只有当您尝试使用类似的东西以未装箱(并且必须严格)的形式实现数组时才会发生实际分配computeUnboxedP。 IIRC 延迟数组并不严格。

标签: arrays haskell data-structures lazy-evaluation repa


【解决方案1】:

要考虑的一个库是reparepa 中的关键思想之一是 Array 类型在其底层表示上进行了参数化。其中一个示例是

  • 延迟表示 D 用于“不真实”的数组 - 它们只是按需计算元素;
  • 未装箱数据的未装箱矢量表示U
  • 盒装数据的盒装矢量表示 V(仅在您无法创建数据的 Unbox 实例时使用)
  • 对外部缓冲区 F 的引用,如果您想写入一些外部位图图形缓冲区,则特别有用

当您编译代码时,延迟数组将被优化掉。不用说,未装箱的数组具有O(1) 访问权限。对于您的情况,您可能只会使用形状为 DIM2 的数组(二维数组)。

出于演示目的,这里有一个程序,它的功能与您想要的类似:它接收位图图层列表及其偏移量,以及背景位图。然后,它将这些图层绘制在位图背景之上。它依赖于repa-io 将图像加载到repa 数组中。

import Data.Array.Repa.IO.BMP
import Data.Array.Repa.Shape
import Data.Array.Repa
import Data.Word

-- This is our internal representation of a bitmap
type Image s = Array s DIM2 (Word8, Word8, Word8)
data Layer s = Layer { image :: Image s, offset :: (Int, Int) }

drawLayersOnBg :: Image U -> [Layer U] -> Image D
drawLayersOnBg background layers = foldl (\bg (Layer im (x,y)) -> overlay bg (ix2 y x) im) (delay background) layers
  where
    overlay :: Image D -> DIM2 -> Image U -> Image D
    overlay bg off ov = backpermuteDft bg
                                       (\i -> let i' = i `subDim` off
                                                 in if extent ov `inShape` I'
                                                      then Just i'
                                                      else Nothing)
                                       ov

    subDim :: DIM2 -> DIM2 -> DIM2
    subDim (Z :. y :. x) (Z :. dy :. dx) = ix2 (y - dy) (x - dx)

main = do
  Right bg <- readImageFromBMP "background.bmp"
  Right l1 <- readImageFromBMP "layer1.bmp"
  Right l2 <- readImageFromBMP "layer2.bmp"
  Right l3 <- readImageFromBMP "layer3.bmp"

  out <- computeP $ drawLayersOnBg bg [ Layer l1 (200,100) , Layer l2 (100, 200), Layer l3 (-300,400) ]
  writeImageToBMP "out.bmp" out

通过互联网上的一些随机位图,我得到了这个作为输出:

如果我愿意,我什至可以使用fromForeignPtr 直接从某个外部内存缓冲区读取位图,并使用computeIntoP 而不是computeP 将位图输出直接写入某个外部内存缓冲区(无中间分配) .如果您需要高性能,这些特别有趣。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-06
    • 2011-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    • 2012-04-24
    相关资源
    最近更新 更多