Haskell列表帮助!

Jam*_*es1 0 haskell

我正在努力使用Haskell编程.

我有下面的列表,我想让它堆叠在一起,所以它就像3 x 4像素的图像.例如:

像素图像

以及如何更改第一行或第二行的值...例如:说我想让它更暗或更白(0代表黑色,255代表白色)

type Pixel = Int
type Row = [Pixel]
type PixelImage = [Row]
print :: PixelImage 
print = [[208,152,240,29],[0,112,255,59],[76,185,0,152]]
Run Code Online (Sandbox Code Playgroud)

我在这里得到的代码没有堆叠列表,我不知道如何堆叠它.

请帮忙,我真的很挣扎.

提前致谢!

Gre*_*con 8

使用print图像以外的名称可以避免与Prelude的冲突print.

type Pixel = Int
type Row = [Pixel]
type PixelImage = [Row]

img :: PixelImage 
img = [[208,152,240,29],[0,112,255,59],[76,185,0,152]]
Run Code Online (Sandbox Code Playgroud)

这是一种低效的表示,但它可以用于学习练习.

您可以打印一个PixelImage堆叠在一起的行,并在源代码顶部添加一些导入和I/O操作:

import Control.Monad
import Data.List
import Text.Printf

printImage :: PixelImage -> IO ()
printImage img =
  forM_ img $ putStrLn . intercalate "  " . map (printf "%3d")
Run Code Online (Sandbox Code Playgroud)

这可能看起来令人生畏,但那里的一切都很熟悉.单词顺序有点搞笑.对于每一个RowPixelImage(forM_,很像for在其他语言中环)我们打印(使用putStrLn)的列表中Pixel有两个空格分隔值(感谢intercalate)和左填充空格令匀3个字符的字段(printf).

有了你问题的图片,我们得到了

ghci> printImage img
208  152  240   29
  0  112  255   59
 76  185    0  152

Haskell列表是不可变的:您不能就地修改或破坏性修改.相反,可以考虑制作一个与原始列表相同的列表,但指定的行除外.

modifyRow :: PixelImage -> (Row -> Row) -> Int -> PixelImage
modifyRow img f i = map go (zip [0..] img)
  where go (j,r) | i == j    = f r
                 | otherwise = r
Run Code Online (Sandbox Code Playgroud)

这使你的函数有机会火每个RowPixelImage.假设您要将特定行清零:

ghci> printImage $ modifyRow img (map $ const 0) 0
  0    0    0    0
  0  112  255   59
 76  185    0  152

反转一行是

ghci> printImage $ modifyRow img reverse 0
 29  240  152  208
  0  112  255   59
 76  185    0  152

在另一种语言中,你可能会写img[2] = [1,2,3,4],但在Haskell中它是

ghci> modifyRow img (const [1..4]) 2
[[208,152,240,29],[0,112,255,59],[1,2,3,4]]

这种用法并不十分令人回味,因此我们可以setRow根据modifyRow函数式编程中的常用技术进行定义.

setRow :: PixelImage -> Row -> Int -> PixelImage
setRow img r i = modifyRow img (const r) i
Run Code Online (Sandbox Code Playgroud)

更好:

ghci> printImage $ setRow img [4,3,2,1] 1
208  152  240   29
  4    3    2    1
 76  185    0  152

也许您想要缩放像素值.

scaleRow :: (RealFrac a) => PixelImage -> a -> Int -> PixelImage
scaleRow img x i = modifyRow img f i
  where f = let clamp z | z < 0     = 0
                        | z > 255   = 255
                        | otherwise = truncate z
              in map (clamp . (x *) . fromIntegral)
Run Code Online (Sandbox Code Playgroud)

例如:

ghci> printImage $ scaleRow img 0.5 1
208  152  240   29
  0   56  127   29
 76  185    0  152

添加scaleImage至缩放因子应用到每个Pixel在一个PixelImage比特重构的,以避免重复在多个地方相同的代码的装置.我们希望能够使用

scaleImage :: (RealFrac a) => a -> PixelImage -> PixelImage
scaleImage x = map $ scaleOneRow x
Run Code Online (Sandbox Code Playgroud)

得到,说

ghci> printImage $ scaleImage 3 img 
255  255  255   87
  0  255  255  177
228  255    0  255

这意味着scaleOneRow应该

scaleOneRow :: (RealFrac a) => a -> Row -> Row
scaleOneRow x = map (clamp . (x *) . fromIntegral)
Run Code Online (Sandbox Code Playgroud)

这促进clampPixel价值的顶级功能.

clamp :: (RealFrac a) => a -> Pixel
clamp z | z < 0     = 0
        | z > 255   = 255
        | otherwise = truncate z
Run Code Online (Sandbox Code Playgroud)

这反过来简化了scaleRow:

scaleRow :: (RealFrac a) => PixelImage -> a -> Int -> PixelImage
scaleRow img x i = modifyRow img (scaleOneRow x) i
Run Code Online (Sandbox Code Playgroud)