Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Use a name other than <code>print</code> for your image to avoid collisions with the Prelude's <code>print</code>.</p> <pre><code>type Pixel = Int type Row = [Pixel] type PixelImage = [Row] img :: PixelImage img = [[208,152,240,29],[0,112,255,59],[76,185,0,152]] </code></pre> <p>This is an inefficient representation, but it will do for a learning exercise.</p> <p>You could print a <code>PixelImage</code> with the rows stacked on top of one another with a few imports at the top of your source and an I/O action:</p> <pre><code>import Control.Monad import Data.List import Text.Printf printImage :: PixelImage -&gt; IO () printImage img = forM_ img $ putStrLn . intercalate " " . map (printf "%3d") </code></pre> <p>That may look intimidating, but everything there is familiar. The word order is just a little funny. For each <code>Row</code> in the <code>PixelImage</code> (<a href="http://www.haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/Control-Monad.html#v%3AforM_" rel="nofollow noreferrer"><code>forM_</code></a>, a lot like a <code>for</code> loop in other languages) we print (with <a href="http://haskell.org/ghc/docs/6.12.1/html/libraries/base-4.2.0.0/Prelude.html#v:putStrLn" rel="nofollow noreferrer"><code>putStrLn</code></a>) the list of <code>Pixel</code> values separated by two spaces (thanks to <a href="http://haskell.org/ghc/docs/6.12.1/html/libraries/base-4.2.0.0/Data-List.html#v:intercalate" rel="nofollow noreferrer"><code>intercalate</code></a>) and left-padded with spaces to make uniform 3-character fields (<a href="http://haskell.org/ghc/docs/6.12.1/html/libraries/base-4.2.0.0/Text-Printf.html#v:printf" rel="nofollow noreferrer"><code>printf</code></a>).</p> <p>With the image from your question, we get</p> <pre>ghci> printImage img 208 152 240 29 0 112 255 59 76 185 0 152</pre> <p>Haskell lists are <em>immutable</em>: you cannot modify one in-place or destructively. Instead, think of it in terms of making a different list that's identical to the original except for the specified row.</p> <pre><code>modifyRow :: PixelImage -&gt; (Row -&gt; Row) -&gt; Int -&gt; PixelImage modifyRow img f i = map go (zip [0..] img) where go (j,r) | i == j = f r | otherwise = r </code></pre> <p>This gives your function a chance to fire for each <code>Row</code> in the <code>PixelImage</code>. Say you want to zero out a particular row:</p> <pre>ghci> printImage $ modifyRow img (map $ const 0) 0 0 0 0 0 0 112 255 59 76 185 0 152</pre> <p>Reversing a row is</p> <pre>ghci> printImage $ modifyRow img reverse 0 29 240 152 208 0 112 255 59 76 185 0 152</pre> <p>In another language, you might write <code>img[2] = [1,2,3,4]</code>, but in Haskell it's</p> <pre>ghci> modifyRow img (const [1..4]) 2 [[208,152,240,29],[0,112,255,59],[1,2,3,4]]</pre> <p>That usage isn't terribly evocative, so we can defined <code>setRow</code> in terms of <code>modifyRow</code>, a common technique in functional programming.</p> <pre><code>setRow :: PixelImage -&gt; Row -&gt; Int -&gt; PixelImage setRow img r i = modifyRow img (const r) i </code></pre> <p>Nicer:</p> <pre>ghci> printImage $ setRow img [4,3,2,1] 1 208 152 240 29 4 3 2 1 76 185 0 152</pre> <p>Maybe you want to scale the pixel values instead.</p> <pre><code>scaleRow :: (RealFrac a) =&gt; PixelImage -&gt; a -&gt; Int -&gt; PixelImage scaleRow img x i = modifyRow img f i where f = let clamp z | z &lt; 0 = 0 | z &gt; 255 = 255 | otherwise = truncate z in map (clamp . (x *) . fromIntegral) </code></pre> <p>For example:</p> <pre>ghci> printImage $ scaleRow img 0.5 1 208 152 240 29 0 56 127 29 76 185 0 152</pre> <p>Adding <code>scaleImage</code> to apply a scaling factor to each <code>Pixel</code> in a <code>PixelImage</code> means a bit of refactoring to avoid repeating the same code in multiple places. We'd like to be able to use</p> <pre><code>scaleImage :: (RealFrac a) =&gt; a -&gt; PixelImage -&gt; PixelImage scaleImage x = map $ scaleOneRow x </code></pre> <p>to get, say</p> <pre>ghci> printImage $ scaleImage 3 img 255 255 255 87 0 255 255 177 228 255 0 255</pre> <p>This means <code>scaleOneRow</code> should be</p> <pre><code>scaleOneRow :: (RealFrac a) =&gt; a -&gt; Row -&gt; Row scaleOneRow x = map (clamp . (x *) . fromIntegral) </code></pre> <p>which promotes <code>clamp</code> to a toplevel function on <code>Pixel</code> values.</p> <pre><code>clamp :: (RealFrac a) =&gt; a -&gt; Pixel clamp z | z &lt; 0 = 0 | z &gt; 255 = 255 | otherwise = truncate z </code></pre> <p>This in turn simplifies <code>scaleRow</code>:</p> <pre><code>scaleRow :: (RealFrac a) =&gt; PixelImage -&gt; a -&gt; Int -&gt; PixelImage scaleRow img x i = modifyRow img (scaleOneRow x) i </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload