Exercises for Week 4: IO, Test Data and Properties
Here are some exercises designed to help you practice programming with IO,
test data generation, and properties.
If you do not have time to do all these exercises, don't worry.
The exercises are intended to provide enough work to keep the most
experienced students busy. If you do all exercises marked with an (*) you have
probably understood this week's material.
0 (*). Basic IO
(Questions based on Thompson, Chapter 18).
A. Write an IO program which will first read a positive
integer, say n
, and then reads n
integers and writes
their sum. The program should prompt appropriately for its inputs and
explain its output.
B. Write a program which repeatedly reads integers (one per
line) until finding a zero value and outputs a sorted version of the
inputs read. Which sorting algorithm is most appropriate in such a
case?
C. Define the function
repeat :: IO Bool -> IO () -> IO ()
such that repeat test op
has the effect of
repeating op
until the condition test
is True
.
1 (*). Properties of the Look Function
Consider the following standard Haskell function, that looks up an element in a list of pairs (table):
look :: Eq a => a -> [(a,b)] -> Maybe b
look x [] = Nothing
look x ((x',y):xys)
| x == x' = Just y
| otherwise = look x xys
Define a property prop_LookNothing
that expresses that if the look function
delivers Nothing
,
then the thing we were looking for was not in the table.
Also define a property prop_LookJust
that expresses that if the
look function delivers a result Just
y,
then the pair (x,y) should have been in the table.
Also write a property prop_Look
that combines
prop_LookNothing
and prop_Just
into one property.
2. Monadic helper functions
Give an implementation of the following functions:
sequence :: Monad m => [m a] -> m [a]
mapM :: Monad m => (a->m b) -> [a] -> m [a]
onlyIf :: Monad m => Bool -> m () -> m ()
sequence
takes a list of instructions resulting in a value of type
a, and creates one big instruction that executes all of these,
gathering all results into one result list. Example: the instructions
sequence [ readFile file | file <- files ]
mapM readFile files
both reads the contents of all files in the list files
, and
produces the contents of each of the files.
onlyIf
takes a boolean and an instruction, and creates an
instruction that only executes the argument instruction if the boolean was
True
. If the boolean was False
, nothing happens.
For example,
onlyIf failed tryAgain
executes the instructions
tryAgain only if the boolean failed
is True
.
Hint:
You might find it easier to think of the above functions having type:
sequence :: [IO a] -> IO [a]
mapM :: (a->IO b) -> [a] -> IO [b]
onlyIf :: Bool -> IO () -> IO ()
3 (*). The Number Guessing Game
In this exercise, you are going to implement the "number guessing game"
in Haskell.
Here is an example of how this might work:
Main> game
Think of a number between 1 and 100!
Is it 50? higher
Is it 75? lower
Is it 62? lower
Is it 56? yes
Great, I won!
The text that looks like this
is what the user types in. The other text is produced by your program.
Implement a function
game :: IO ()
That plays one instance of this game.
You might need the following functions:
getLine :: IO String -- reads a line of user input
putStrLn :: String -> IO () -- outputs one line of text
Before you start programming, think of a good guessing strategy for the
computer that minimizes the number of guesses!
4. A Backup Script
The library module System.Directory
provides functions
for working with files and directories.
Use these functions to write a program that
- creates a new directory called "backup",
- copies all the files in the current directory into the backup directory.
Hints: One way to find out what functions
a module contains is so use the :browse
command in GHCi.
Prelude> :module System.Directory
Prelude System.Directory> :browse
...
createDirectory :: FilePath -> IO ()
doesDirectoryExist :: FilePath -> IO Bool
...
This gives you the names and types of the functions, but you probably
still need to consult the documentation to find out how to use them.
Here's the link to the documentation of the modules
included with the latest version of GHC:
You will perhaps also need to perform all of a list of actions.
You may find the function sequence
from exercise 2 useful
for this.
5 (*). Generating Lists
Sometimes we want to generate lists of a certain length.
A. Write a generator
listOf :: Integer -> Gen a -> Gen [a]
such that listOf
n g
generates a list of n elements, where each element is
generated by g. What property would you
write to test that your generator behaves as it should?
B. Now use listOf
to write a generator that generates pairs of lists of the
same, random, length.
C. Take a look at the standard Haskell functions
zip
and unzip
:
zip :: [a] -> [b] -> [(a,b)]
unzip :: [(a,b)] -> ([a],[b])
Write down two properties for these; one that says that zip
is
the inverse of unzip
, and one that unzip
is the
inverse of zip
. Note that unzip
is not always the
inverse of zip
, so you need a condition! Could you make use of
the generator you just defined?
6. Generating Ordered Lists
A.Write a function ordered
that checks if a list is
ordered; each element in the list should be
smaller than or equal to the next element.
B.
Write a generator that generates random lists of integers that are sorted
(ordered).
You are not allowed to make use of the function sort!
(And not of the generator orderedList from the lecture either...)
Hint: First generate a list of random, positive integers. Then,
generate a first, random number in the list. Then, produce a list
where the differences between each consecutive pair of elements is
given by the list of positive integers.