Functional equivalent to iterating over a 2D array
I have this function in Haskell (I am using the Haskell-SDL library):
pixel :: Surface -> Int16 -> Int16 -> Pixel -> IO Bool
pixel screen x y color
I want to use this to take a 2D array (or some other kind of data structure) and dra开发者_开发技巧w it to the screen, one pixel at a time. I looked into doing it with forM_
but can't figure out how to get it to operate on the x and y arguments.
I'm pretty new to Haskell and functional programming in general. I'm working my way through Yet Another Haskell Tutorial but this problem just has me stumped.
In case it's relevant to the solution, I'm trying to write a raytracer. It has to perform a calculation for each pixel, then write that pixel to the screen.
If you are using nested lists, do:
import Control.Monad
plot :: Surface -> [[Pixel]] -> IO ()
plot surf = zipWithM_ (\ y -> zipWithM_ (\ x c -> pixel surf x y c) [0..]) [0..]
You need to split two problems here. First you need to compute the pixel value. This should be a pure function of the scene and the coordinate of the ray you are firing into it. Then you need to write that pixel value to the screen.
So first you want a function:
type Coord = (Int, Int)
raytrace :: Scene -> Coord -> (Coord, Colour)
-- You will see why it returns this pair in a few lines
Then you want to call that function for every pixel in your surface, to get a list of coordinate-colour pairs:
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)
And finally put the list of pixels onto the display surface using your "pixel" function.
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
The only thing is, your "pixel" function returns an "IO Bool". I don't know why, so I've ignored it by using "mapM_" rather than "mapM".
This looks like it builds a horribly inefficient list of coordinate-colour pairs and then iterates through it to draw the picture. But in fact, thanks to the lazy nature of Haskell, it actually compiles down to a loop that generates each colour and then calls "pixel" on the result.
Haskell has real arrays of various kinds. To me it seems like you're doing something quite inefficient, you want to create a pixel buffer and blit it to another buffer? it would be much more efficient to create a new SDL surface (for which you can get/set pixels) and blit that to another surface/screen or create a real array of pixels and create SDL surface with that then use SDL's blit routines. If you explain what you're trying to do I think we can show you a better way.
精彩评论