Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Summary: This lecture describes the construction of a Haskell program that specifies the behaviour of a supermarket checkout. Reference: Thompson Section 6.4
A supermarket checkout A supermarket checkout allows the operator to scan the barcodes of a basket of items, and produces an itemised bill for the customer The behaviour of the checkout is specified by a computer program The input to the checkout program is a sequence of the barcodes from the scanned items The output from the checkout program is a bill that gives the name and price of each scanned item, the total cost of the basket of items, and possibly other details of the transaction In this lecture we will develop a Haskell program for a supermarket checkout
1 of 11
2 of 11
Types The first decision is how to represent the various sorts of objects used in the program The price of an item is best represented as an integer number of cents
type Price = Int
this allows more precision than using a real number The name of an item is represented as a string
type Name = String
The supermarket maintains a database that records the above three bits of information for each stocked item
type Item = (Barcode, Name, Price)
Types contd. The basket of items bought by a customer is fed into the program as a sequence of barcodes
type Basket = [Barcode]
We also need an "internal" representation of a basket that contains the information required to print the bill for the basket
type BillType = [(Name, Price)]
We also define a function lineLength that specifies the width of the printed bill
lineLength :: Int -- lineLength returns the width of a bill lineLength = 30
The width is defined using a constant function for three reasons because the name lineLength is much easier to understand than a constant like 30, c.f. "magic numbers" so that all functions that use the width always refer to the same width, and so that it is easy to change the width when required only the definition of lineLength needs to be changed
4 of 11
The top-level function The top-level function of a program is the one that "calls" all of the other functions The top-level function for the checkout has the following definition
checkout :: Basket -> String -- checkout t returns the bill for t checkout t = formatBill (makeBill t)
Where does this equation come from? We have decomposed the problem into two parts makeBill uses the database to turn a Basket into the corresponding BillType
makeBill :: Basket -> BillType -- makeBill t returns the internal -- bill rep. for t
formatBill :: BillType -> String -- formatBill b returns the bill -- to be printed for b
5 of 11
Making a bill Making a bill (i.e. the internal rep.) involves searching the database for the information associated with each barcode in the basket
makeBill :: Basket -> BillType -- makeBill t returns the internal -- bill rep. for t makeBill t = [look database bc | bc <- t]
look is the function that actually does the searching given a barcode and a database, look locates all
entries in the database that refer to the barcode obviously there should be exactly one entry for each barcode, but
look :: Database -> Barcode -> (Name, Price) -- look d bc returns the name and price -- of bc in d look d bc | length nps == 1 = head nps | length nps == 0 = (unwords["Unknown item", show bc],0) | length nps > 1 = (unwords["Duplicated item",show bc],0) where nps = [(n, p) | (bc', n, p) <- d, bc' == bc]
6 of 11
Formatting a bill A printed bill contains the title, one line for each item purchased, and one line for the total The title is just a string containing the name of the supermarket
formatTitle :: String -- formatTitle returns the title line -- for the bill formatTitle = "Haskell Stores"
The line for an item has lineLength characters in the following form
Fish fingers..............1.21
What parts are there in a line? the name of the item, some number of dots, and the price of the item So formatLine has the definition
formatLine :: (Name, Price) -> String -- formatLine np returns the line of -- the bill for np formatLine (n, p) = n ++ replicate (lineLength - length n - length p') '.' ++ p' where p' = formatPrice p
7 of 11
Formatting a bill
formatPrice turns a price into a string formatPrice :: Price -> String -- formatPrice p returns a string -- containing p formatPrice p = show (p `div` 100) ++ "." ++ formatCents (p `mod` 100) formatCents turns a number of cents into a string
formatCents :: Int -> String -- formatCents x returns a string -- containing x formatCents x | x < 10 = '0' : show x | x >= 10 = show x formatLines returns a string containing the lines for all
formatLines :: BillType -> String -- formatLines b returns the lines -- for the individual items b formatLines b = unlines [formatLine np | np <- b]
8 of 11
Calculating and formatting the total The total is of course just the sum of the prices of the individual items
makeTotal :: BillType -> Price -- makeTotal b returns the total cost of b makeTotal b = sum [p | (n, p) <- b]
why duplicate the effort? this approach (i.e. re-using formatLine) also guarantees consistency in the output
formatTotal :: Price -> String -- formatTotal p returns the line -- for the total p formatTotal p = formatLine ("Total", p)
9 of 11
Formatting the complete bill To recap: a printed bill contains the title, one line for each item purchased, and one line for the total formatBill "glues" all of the previous functions together to build these three components
formatBill :: BillType -> String -- formatBill b returns the bill -- to be printed for b formatBill b = unlines [formatTitle ,"" ,formatLines b ,"" ,formatTotal (makeTotal b)]
10 of 11
The complete program That completes the program! The complete program is available on the unit web-site
11 of 11