Input and Output

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

  2. 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. 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. 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. 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.

    • Write a program that first asks for the name of the target file, and then continues asking for names of files to be appended to that file until an empty line is entered. Note that the target files may be one of the source files!

    • If we know that none of the source files equals the target file we may do a bit better using the function appendFile from System.IO. Change the function you have written above using this function. What are the advantages and disadvantages of this approach?

Loading Binary Trees from a File

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

  1. 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"
  2. 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)