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
y
Top-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 -> Double
improve guess x
must always yield better guess (approach base case)
improve
Refine 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) / 2
new guess average guess (x/guess)
closer to exact solution
goodEnough
Refine 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
.