7

想象一下一个命令式渲染引擎,它将精灵 blits 到稍后显示的位图。这在很大程度上依赖于有效地改变所述位图中的单个像素的能力。我将如何在没有副作用的情况下做这样的事情?我猜需要一个完全不同的数据结构?

4

3 回答 3

5

您可以将任何使用可变状态的算法转换为将状态与其一起“串起来”的算法。Haskell 提供了一种这样做的方法,它仍然感觉像使用状态 Monad 进行命令式编程。

虽然,在我看来,基本的 blit 操作可以以更实用的方式完成。您基本上是通过逐像素操作组合两个位图以生成新位图。这对我来说听起来很实用。

高质量的命令式代码通常比好的函数式代码更快,但如果你愿意放弃一点速度,你通常可以以纯函数式风格创建非常好的架构

于 2011-09-02T21:29:25.563 回答
4

Haskell 有副作用,你应该在适当的时候使用它们。将在您的内部循环中(因此对性能至关重要)的高速 blit 例程肯定是适合突变的地方,所以使用它!你有几个选择:

  • 使用 ST(U)Array 或 IO(U)Array 在 Haskell 中滚动你自己的。不建议。
  • 在 C 中滚动你自己的,并用 FFI 调用它。不建议。
  • 使用已经提供这种操作的众多图形工具包之一,并且花费数百个程序员时间来制作具有高性能的良好界面,例如 Gtk 或 OpenGL。强烈推荐。

享受!

于 2011-09-02T21:39:27.650 回答
1

表示图像的一种自然函数式方法是使用索引函数:

Image :: (Int,Int) -> Color

使用这种表示,将一个区域从一个图像到另一个图像将通过以下方式实现

blit area a b = \(x,y) -> if (x,y) `isInsideOf` area then a (x,y) else b (x,y)  

如果需要平移或其他变换,可以直接应用于坐标:

translate (dx,dy) image = \(x,y) ->  b (x+dx,y+dy)  

这种表示为您提供了使用图像点的自然方式。例如,您可以轻松地处理非矩形区域,并执行一些技巧,例如将图像插值作为单独的函数,而不是成为通常图像缩放算法的一部分:

quadraticInterpolation :: ((Int,Int) -> Color) -> ((Double,Double) -> Color)

在某些情况下,性能可能会受到影响,例如当您将多个图像拼合为一张然后对结果进行计算时。这会导致每个连续计算的每个像素的测试链。但是,通过应用记忆化,我们可以暂时将函数表示呈现为数组并将其转换回它的索引函数,从而消除连续操作的性能损失。

请注意,记忆也可用于将并行性引入流程。

于 2011-09-03T05:25:51.437 回答