Haskell 函数等价于迭代二维数组

Haskell 函数等价于迭代二维数组,haskell,functional-programming,Haskell,Functional Programming,我在Haskell中有此函数(我正在使用Haskell SDL库): pixel::Surface->Int16->Int16->pixel->IO Bool 像素屏幕x y颜色 我想用它来获取一个2D数组(或其他类型的数据结构)并将其绘制到屏幕上,一次一个像素。我研究过如何使用forM\uuu实现它,但不知道如何让它对x和y参数进行操作 一般来说,我对Haskell和函数式编程相当陌生。我正在努力学习另一个Haskell教程,但这个问题让我难住了 如果它与解决方案有关,我正在尝试编写一个光线跟

我在Haskell中有此函数(我正在使用Haskell SDL库):

pixel::Surface->Int16->Int16->pixel->IO Bool

像素屏幕x y颜色

我想用它来获取一个2D数组(或其他类型的数据结构)并将其绘制到屏幕上,一次一个像素。我研究过如何使用
forM\uuu
实现它,但不知道如何让它对x和y参数进行操作

一般来说,我对Haskell和函数式编程相当陌生。我正在努力学习另一个Haskell教程,但这个问题让我难住了


如果它与解决方案有关,我正在尝试编写一个光线跟踪器。它必须对每个像素执行计算,然后将该像素写入屏幕。

如果使用嵌套列表,请执行以下操作:

import Control.Monad
plot :: Surface -> [[Pixel]] -> IO ()
plot surf = zipWithM_ (\ y -> zipWithM_ (\ x c -> pixel surf x y c) [0..]) [0..]

Haskell有各种各样的真实数组。对我来说,你似乎在做一些效率很低的事情,你想创建一个像素缓冲区并将它放到另一个缓冲区吗?创建一个新的SDL曲面(您可以为其获取/设置像素)并将其blit到另一个曲面/屏幕,或者创建一个真正的像素数组并使用该数组创建SDL曲面,然后使用SDL的blit例程,效率会更高。如果你解释一下你想做什么,我想我们可以给你一个更好的方法。

你需要在这里分两个问题。首先需要计算像素值。这应该是场景的一个纯粹的函数,以及向其中发射的光线的坐标。然后您需要将该像素值写入屏幕

因此,首先需要一个函数:

type Coord = (Int, Int)

raytrace :: Scene -> Coord -> (Coord, Colour)  
    -- You will see why it returns this pair in a few lines
然后,您希望为曲面中的每个像素调用该函数,以获得坐标颜色对的列表:

allCoords :: Int -> Int -> [Coord]
allCoords width height = [(x,y) | x <- [0..width], y <- [0..height]]

allPixels :: Scene -> Int -> Int -> [(Coord, Colour)]
allPixels scene w h = map (raytrace scene) (allCoords w h)
唯一的问题是,你的“像素”函数返回一个“IO布尔”。我不知道为什么,所以我用“mapM_”而不是“mapM”忽略了它


这看起来像是它构建了一个效率极低的坐标颜色对列表,然后通过迭代来绘制图片。但事实上,由于Haskell的惰性,它实际上会编译成一个循环,生成每种颜色,然后在结果上调用“像素”。

你知道如何在Haskell中表示2D数组吗?我相信它可以通过嵌套列表来实现,比如:[[1,2,3],[4,5,6],[7,8,9]]Haskell有各种合适的数组,使用嵌套的像素列表不是一个好主意。检查我下面的答案。我正在试着写一个光线跟踪器。它必须对每个像素进行计算,然后将该像素写入屏幕。@chuzzum我建议您要么读取/写入屏幕后缓冲区,要么创建一个空SDL曲面,它只是一个像素缓冲区(您可以操纵像素)和blit(blitSurface)将该曲面复制到后缓冲区,或者创建一个可变的未固定数组,对于该数组,您有各种选项,例如IOUArray、STUArray、StorableArray、unbox.MVector(),然后将像素复制到后缓冲区。SDL有一个函数,可以从数组创建SDL曲面,haskell绑定也公开了这一点。使用2D像素列表真的是个坏主意。这样做是否有主要的性能优势?因为我要做的唯一一件事就是对它进行迭代,所以我不明白为什么列表比数组更糟糕或更好。例如,与不可变列表相比,使用可变数组有什么优势?但我确实认为直接写入屏幕可能是最好的解决方案。还有不可变数组,以及装箱和未装箱的变体。与2D列表相比,非固定数组的主要优点是1。对于非固定数组,即使有多个维度,也有一个连续的内存块。2.更好的缓存一致性,2D链表将有非常差的缓存一致性。3.较小的内存消耗,列表节点被单独分配,以及它们的元素,所有这些元素都将分散在内存中。3.对于某些类型的Haskell数组,您只需将数组指针传递给C绑定库,而无需任何开销和…4。即使在列表上迭代也会比数组稍微慢一点,但这将是一个微不足道的区别。5.索引O(1),只要知道多维数组的所有索引。在2D列表中建立索引不是一个O(N^2)操作吗?我并不是说你应该避免列表,只是因为它们在某些情况下是不合适的,并且这种情况下,抑制像素缓冲的情况下,当你有数组时,我不会认为2D列表是个好主意。
writeScene :: Surface -> Scene -> Int -> Int -> IO ()
writeScene surface scene w h = mapM_ writePixel (allPixels scene w h)
    where writePixel ((x,y),c) = pixel surface x y c