Используйте изображение, отличное от print
, чтобы использовать это изображение, чтобы избежать столкновений с Prelude's 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]]
Это неэффективное представление, но оно будет использоваться для обучения.
Вы можете напечатать PixelImage
с рядами сложены друг на друга с несколькими импорта в верхней части источника и действия I/O:
import Control.Monad
import Data.List
import Text.Printf
printImage :: PixelImage -> IO()
printImage img =
forM_ img $ putStrLn . intercalate " " . map (printf "%3d")
Это может выглядеть пугающим, но там все знаком. Порядок слов немного забавен. Для каждого в PixelImage
(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
Это дает ваша функция шанс сгореть для каждого в PixelImage
. Допустим, вы хотите обнулить конкретную строку:
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
Nicer:
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)
Например:
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
, чтобы получить, скажем
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)
, который способствует clamp
для верхнего уровня функции на Pixel
значений.
clamp :: (RealFrac a) => a -> Pixel
clamp z | z < 0 = 0
| z > 255 = 255
| otherwise = truncate z
Это, в свою очередь, упрощает scaleRow
:
scaleRow :: (RealFrac a) => PixelImage -> a -> Int -> PixelImage
scaleRow img x i = modifyRow img (scaleOneRow x) i
Здесь нет действительно заметного вопроса. Вы сделали попытку, которая не работает? Вы просите общий подход? – Gian
да, я прошу о помощи, потому что я не знаю, как это сделать, так как я очень новичок в haskell – James1
Дайте нам пример вывода, который вы хотите получить для данного ввода. – dave4420