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 ()
= do putStrLn "Path to file:"
linesAndWords <- getLine
fp <- readFile fp
fileContents let n = length . lines $ fileContents
= length . words $ fileContents
m putStrLn $ "Number of lines: " ++ show n
putStrLn $ "Number of words: " ++ show m
main :: IO ()
= linesAndWords main
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_
.
= do putStr "Give a number: "
tabulate <- getInt
n mapM_ (putStrLn . f n) [1..10]
putStrLn "Goodbye"
where
f :: Int -> Int -> String
= unwords [ show i, "*", show n, "=", show $ i * n] f n i
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
.
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
fromSystem.IO
. Change the function you have written above using this function. What are the advantages and disadvantages of this approach?
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:
- Loads the tree from “myTree.hs” in memory
- asks the user for an number (an Int)
q
- prints if
q
appears in the tree or not.
You may use that
- the function
readFile :: FilePath -> IO String
can load the data from a file (as specified by the path) into aString
, - that the function
putStrLn :: String -> IO ()
can print aString
to standard output, and - that the function
getLine :: IO String
that can read aString
from standard input.
7 atHome
Write a function readTree :: IO (Tree Int)
that only performs
step 1. from the above description
readTree :: IO (Tree Int)
= do s <- readFile "myTree.txt"
readTree return $ read s
-- or using that 'IO' is a Functor:
= fmap read $ readFile "myTree.txt"
readTree -- or
= read <$> readFile "myTree.txt" readTree
8 atHome
Now write a full implementation of main :: IO ()
that uses
readTree
and performs all steps of the program sketched above.
main :: IO ()
= do
main <- readTree
t putStrLn "Please input a number."
<- getLine
qs let q = read qs
putStrLn $ show (q `elem` elems t)
-- or, again using that IO is a Functor:
main :: IO ()
= do
main <- readTree
t putStrLn "Please input a number."
<- read <$> getLine
q putStrLn $ show (q `elem` elems t)