Input and Output

1 atHome

Extend the “guess a number” game to generate the bounds randomly.

2 atHome

Write sequence and sequence_ using (>>=) instead of do-notation.

sequence          :: [IO a] -> IO [a]
sequence []       = return []
sequence (a:acts) = a >>= \x -> sequence acts >>= \xs -> return (x:xs)

-- or using a foldr:
sequence :: [IO a] -> IO [a]
sequence = foldr (\a r -> a >>= \x -> r >>= \xs -> return (x:xs)) (return [])

-- sequence_ just throws away the results:
sequence_ :: [IO ()] -> IO ()
sequence_ = foldr (\a r -> a >>= \_ -> r) (return ())

-- or even:
sequence_ :: [IO ()] -> IO ()
sequence_ = foldr (>>) (return ())

3 atHome

Write a function which prompts for a filename, reads the file with this name, splits the file into a number of lines, splits each line in a number of words separated by ' ' (a space), and prints the total number of lines and words.

linesAndWords :: IO ()
linesAndWords = do putStrLn "Path to file:"
                   fp <- getLine
                   fileContents <- readFile fp
                   let n = length . lines $ fileContents
                       m = length . words $ fileContents
                   putStrLn $ "Number of lines: " ++ show n
                   putStrLn $ "Number of words: " ++ show m

main :: IO ()
main = linesAndWords

4 atHome

Given the function getInt :: IO Int, which reads an integer value from standard input, write a program that results in the following input/output behaviour (the 3 has been typed in by the user):

Give a number: 3
1 * 3 = 3
2 * 3 = 6
3 * 3 = 9
...
10 * 3 = 30
Goodbye

Try to use sequence_, mapM_ or forM_.

tabulate = do putStr "Give a number: "
              n <- getInt
              mapM_ (putStrLn . f n) [1..10]
              putStrLn "Goodbye"
  where
    f     :: Int -> Int -> String
    f n i = unwords [ show i, "*", show n, "=", show $ i * n]

5 atHome

Write a function of type [FilePath] −> FilePath −> IO () which concatenates a list of files to a specific target file: the first parameter is a list of filenames and the second parameter the name of the target file. Do not use the function appendFile.

6 atHome

Consider usual type of binary trees

data Tree a = Leaf a
            | Node (Tree a) a (Tree a)
            deriving (Show,Read,Eq)

and let “myTree.txt” be a text file that stores (the string representation as given by Show) of a Tree Int.

We will write a short program main :: IO () that:

  1. Loads the tree from “myTree.hs” in memory
  2. asks the user for an number (an Int) q
  3. prints if q appears in the tree or not.

You may use that

7 atHome

Write a function readTree :: IO (Tree Int) that only performs step 1. from the above description

readTree :: IO (Tree Int)
readTree = do s <- readFile "myTree.txt"
              return $ read s

-- or using that 'IO' is a Functor:
readTree = fmap read $ readFile "myTree.txt"
-- or
readTree = read <$> readFile "myTree.txt"

8 atHome

Now write a full implementation of main :: IO () that uses readTree and performs all steps of the program sketched above.

main :: IO ()
main = do
   t <- readTree
   putStrLn "Please input a number."
   qs <- getLine
   let q = read qs
   putStrLn $ show (q `elem` elems t)

-- or, again using that IO is a Functor:
main :: IO ()
main = do
   t <- readTree
   putStrLn "Please input a number."
   q <- read <$> getLine
   putStrLn $ show (q `elem` elems t)