H. Conrad Cunningham
5 September 2017
Acknowledgements: These slides accompany section 2.5, “Using Top-Down Stepwise Refinement” from Chapter 2 “Basic Haskell Functional Programming” of “Introduction to Functional Programming Using Haskell”.
Advisory: The HTML version of this document may require use of a browser that supports the display of MathML. A good choice as of August 2017 is a recent version of Firefox from Mozilla.
Code: The Haskell module for the Square root case study is in file Sqrt.hs. Limited testing code is in module TestSqrt.hs.
Introduce a development method for programs with several functions
Introduce Haskell modules
Problem: find the number such that
and .
Newton’s method of successive approximations
yTop-down stepwise refinement – adapt general method to Haskell
Start with high-level solution
define one or more functions
include both concrete code (Haskell) and abstract code (pseudocode)
give type signatures of functions
specify inputs, outputs, and termination conditions
Choose an abstract part
refine it into subparts: concrete Haskell and abstract pseudocode
refine data structures if needed
consider relevant criteria (termination, time, space, generality, etc.)
stay with problem terminology and notation
if not a good choice, back up and try other choices
Continue step 2 until all parts executable and meet criteria
Postulate function with signature
sqrtIter :: Double -> Double -> Double
Encode Newton method step 2
sqrtIter guess x
| goodEnough guess x = guess
| otherwise = sqrtIter (improve guess x) x
goodEnough determines when sufficiently close to solution
goodEnough :: Double -> Double -> Bool sqrtIter guess x
| goodEnough guess x = guess
| otherwise = sqrtIter (improve guess x) x
improve generates better guess
improve :: Double -> Double -> Doubleimprove guess x must always yield better guess (approach base case)
improveRefine improve in Newton step 3
improve :: Double -> Double -> Double
improve guess x = average guess (x/guess)
average computes mean of two values
average :: Double -> Double -> Double
average x y = (x + y) / 2new guess average guess (x/guess) closer to exact solution
goodEnoughRefine goodEnough
be wary of inexact floating point arithmetic!
choose tolerance of 0.001 (might not work in all cases)
goodEnough :: Double -> Double -> Bool
goodEnough guess x = abs (square guess - x) < 0.001 abs absolute value function from standard library (Prelude)
square easy (could just be guess * guess)
square :: Double -> Double
square x = x * x Sufficient just to use 1
sqrt' :: Double -> Double
sqrt' x | x >= 0 = sqrtIter 1 x
hide guess argument to sqrtIter
avoid name clash with sqrt function in standard Prelude
Sqrt.hs module Sqrt -- put in file "Sqrt.hs"
(sqrt') -- only export sqrt', others hidden
where
sqrt' :: Double -> Double
sqrt' x | x >= 0 = sqrt_iter 1 x
sqrt_iter :: Double -> Double -> Double
sqrt_iter guess x
| good_enough guess x = guess
| otherwise = sqrt_iter (improve guess x) x
good_enough :: Double -> Double -> Bool
good_enough guess x = abs (square guess - x) < 0.001
square :: Double -> Double
square x = x * x
average :: Double -> Double -> Double
average x y = (x + y) / 2
improve :: Double -> Double -> Double
improve guess x = average guess (x/guess)
Example use of Sqrt module (from Sqrt.hs)
module TestSqrt
where
import Sqrt -- file Sqrt.hs, import all public features
main = do
putStrLn (show (sqrt' 16))
putStrLn (show (sqrt' 2))
Top-down stepwise refinement method
Haskell modules
Newton’s method
The Haskell module for the Square root case study is in file Sqrt.hs. Limited testing code is in module TestSqrt.hs.