A lab exercise for the module CI285 Introduction to Functional Programming at the University of Brighton.
This program "makes change". That is, it works out which combination of coins is required to make a given amount of money. While the program is running it repeatedly asks the user for a number until the user enters an empty line:
$ git clone https://github.com/jimburton/change
$ cd change
$ cabal run change
Preprocessing executable 'change' for change-0.1.0.0...
Running change...
Enter a number and I'll count out the change
127
[Pound,Twenty,Five,Two]
Enter a number and I'll count out the change
13
[Ten,Two,Penny]
Enter a number and I'll count out the change
$
In Change.hs
, the makeChange
function starts with the total amount of money that
we need to make change for. It calculates the number of highest value
coins that can be taken from this amount, then calls itself
recursively with the remainder amount. Study the code so that you
understand it.
There are two test suites in this project, one that uses HUnit and one
that uses QuickCheck. Read the source files in the test
directory to
get an idea of how they work. You can run them separately by name
(cabal test test-hunit
or cabal test test-quickcheck
) or together
(cabal test
).
The argument to Change.makeChange
is the current amount of money that we
are trying to make change for. You will change this program to use the
State
monad to store the remaining amount as a piece of state to be
read and updated in the makeChange
function.
Add the import statement import Control.Monad.State.Lazy
to the top
of Main.hs
and Change.hs
. You also need to tell cabal
about this
dependency. Add monads-tf
(Monad Transformer Library) to the list of
dependencies:
build-depends: base, monads-tf
Then run cabal configure
again. Change the type of makeChange
like so:
makeChange :: State Int [Coins]
This tells us that makeChange
is a function in the State
monad,
that the state being stored is an Int
and that the result of the
state processor will have the type [Coin]
. This function will now
begin by using get
to retrieve the remaining amount, then getCoin
and coinDiv
to work out which coins to add to the list and the new
remaining amount, as before. Use put
to store the new remaining
amount. Note that the recursive call to makeChange
is now monadic so
it will look like this:
rest <- makeChange
Change the main
function to run makeChange
using evalState
and the
number supplied by the user as the initial state:
let i = read str :: Int
coins = evalState makeChange i
To make the tests work you will also need to change the calls to
makeChange
in TestHUnit.hs
and TestQuickCheck.hs
to use
evalState
.