user1006042
user1006042

Reputation: 97

Haskell input and output?

I have a program that I've written in Haskell, and everything except the IO is working. The IO is meant to do the following: - Take in a number from the user - If this number is positive, read that many lines into a list, then take the number and created list and pass it to a function which will return the output - If this number is negative instead, only read the next line, and take the number and that line and pass it to a function which returns other output

I've been using "interpret (unlines . map program . lines)" bit of code for testing purposes, but I'm not sure how to make it handle multiple line input (especially when the number of lines isn't constant)

I would appreciate any help

Ahh yes, and the program loops

More details: The program itself handles quadtrees. Input can be along the lines of this: 8 00000000 00111111 00001111 00001111 00011111 00111100 00111000 In which case the program sees the 8, the size of the picture, and builds its corresponding quadtree. The output is rather convoluted, but basically it turns it into a text representation of the quadtree's structure.

Current program:

module Main where

main = interact (unlines . map program . lines) -- the IO here is bologna, as it doesn't do anything with the user input
--turns ones and zeros into easier to distinguish characters
replace 0 = '.'
replace 1 = '*'
program x = display (toArray 8 (t2)) -- just a test case to see if the important functions work, as well as a placeholder for real IO, hopefully
data Quadtree a = Tip | Node a (Quadtree a) (Quadtree a) (Quadtree a) (Quadtree a) -- the quadtree datastructure
leaf x = Node x Tip Tip Tip Tip -- saves some typing
-- this function takes a quadtree and returns a 2-dimensional list of binary digits to draw the picture
toArray a (Node x Tip Tip Tip Tip) = replicate a (replicate a x) 
toArray a (Node x b c d e) = (zipWith (++) (toArray (div a 2) b) (toArray (div a 2) c)) ++ (zipWith (++) (toArray (div a 2) d) (toArray (div a 2) e))
display x = unlines (map (map replace) x) -- gives a printable form of the two dimensional array
-- function that turns a number from base five
basefive x
    | x < 5 = x
    | x >= 5 = basefive (div x 5) * 10 + (mod x 5)
-- inserts into quadtree
insert [] (Node 0 Tip Tip Tip Tip) = Node 1 Tip Tip Tip Tip
insert (x:xs) (Node 0 Tip Tip Tip Tip)
    | x == '1' = Node 2 (insert xs (leaf 0)) (leaf 0) (leaf 0) (leaf 0)
    | x == '2' = Node 2 (leaf 0) (insert xs (leaf 0)) (leaf 0) (leaf 0)
    | x == '3' = Node 2 (leaf 0) (leaf 0) (insert xs (leaf 0)) (leaf 0)
    | x == '4' = Node 2 (leaf 0) (leaf 0) (leaf 0) (insert xs (leaf 0))
insert (x:xs) (Node a b c d e)
    | x == '1' = Node a (insert xs b) c d e
    | x == '2' = Node a b (insert xs c) d e
    | x == '3' = Node a b c (insert xs d) e
    | x == '4' = Node a b c d (insert xs e)
-- builds quadtree from scratch
insertall [] x = x
insertall (x:xs) y = insert x (insertall xs y)
s1 = "9 14 17 22 23 44 63 69 88 94 113" -- the example quadtree "path"; it represents the structure
s2 = ["00000000","00000000","00001111","00001111","00011111","00111111","00111100","00111000"] -- this represents the image
-- function which splits a list in half
split :: [a] -> ([a],[a])
split xs = go xs xs where
  go (x:xs) (_:_:zs) = (x:us,vs) where (us,vs)=go xs zs
  go    xs   _       = ([],xs)
quarter a b c = ((a . split) (map (b . split) c)) -- function which quarters a 2 dimensional list
-- function which takes a 2 dimensional array and returns its tree
toTree a x
    | x == replicate a (replicate a '1') = (leaf 1)
    | x == replicate a (replicate a '0') = (leaf 0)
    | otherwise = Node 2 (toTree (div a 2) (quarter fst fst x)) (toTree (div a 2) (quarter fst snd x)) (toTree (div a 2) (quarter snd fst x)) (toTree (div a 2) (quarter snd snd x))
t2 = toTree 8 s2

Both s1 and s2 are sample user inputs; the random eights thrown around is the size of the quadtree image, which is supposed to also be supplied by the user

Upvotes: 1

Views: 1311

Answers (1)

sclv
sclv

Reputation: 38893

So you really have a spec for a function:

Take in a number from the user - If this number is positive, read that many lines into a list, then take the number and created list and pass it to a function which will return the output - If this number is negative instead, only read the next line, and take the number and that line and pass it to a function which returns other output.

We can just write that function straightforwardly.

interactiveFun f g = do
       let readInt :: String -> Int
           readInt = read
       x <- fmap readInt getLine
       if x > 0
           then print . f . map readInt =<< replicateM x getLine
           else print . g x . readInt =<< getLine

Edit: <$> is the same an infix fmap, and comes from Control.Applicative. Its idiomatic Haskell, but I've edited it out for simplicity. replicateM comes from Control.Monad. You should import it and use it -- many many everyday Haskell functions come from base libraries other than the Prelude. All those base libraries are part of the standard, are portable, are useful, and should be used liberally.

Upvotes: 4

Related Questions