Recursion and Induction: Lexical Issues; Recursive Programming Greg Plaxton Theory in Programming Practice, Spring 2004 Department of Computer Science University of Texas at Austin
Lexical Issues • Program layout • Function parameters and binding • The where clause • Pattern matching Theory in Programming Practice, Plaxton, Spring 2004
Program Layout • Unlike many programming languages, white space is significant in Haskell • Line indentation is used to delineate the scope of definitions – A definition with initial indentation k is implicitly ended before the first subsequent line with indentation at most k • For example, the following three lines represent a single definition chCase c -- change case | upper c = uplow c | otherwise = lowup c Theory in Programming Practice, Plaxton, Spring 2004
Program Layout: Example • The following (unorthodox) indentation leads to the same definition for the chCase function chCase c -- change case | upper c = uplow c | otherwise = lowup c • The following indentation leads to an error, since the last line is not considered part of the definition of chCase chCase c -- change case | upper c = uplow c | otherwise = lowup c Theory in Programming Practice, Plaxton, Spring 2004
Function Parameters and Binding • Consider the following expression, where f is a function of one argument f 1+1 • In normal mathematics, this will be an invalid expression, and if forced, you will interpret it as f(1+1) • In Haskell, this is a valid expression and it stands for (f 1)+1 Theory in Programming Practice, Plaxton, Spring 2004
Function Parameters and Binding • To apply a function to a sequence of arguments, simply write the function name followed by its arguments; no parentheses are needed unless the arguments are themselves expressions • Some examples involving a function g of two arguments – The expression g x y z stands for (g x y) z – If you write g f x y , it will be interpreted as (g f x) y , so, if you have in mind the expression g(f(x),y) , write it as g (f x) y – You can’t write g(f(x),y) , because (f(x),y) will be interpreted as a pair, which is a single item Theory in Programming Practice, Plaxton, Spring 2004
Function Parameters and Binding • Note that unary minus often requires parentheses • For example, the following expression results in an error inc -2 • It is interpreted as (inc -) 2 , which yields a type error Theory in Programming Practice, Plaxton, Spring 2004
The where Clause • The following function has three arguments, x , y and z , and it determines if x 2 + y 2 = z 2 pythagoras x y z = (x*x) + (y*y) == (z*z) • The definition would be simpler to read in the following form pythagoras x y z = sqx + sqy == sqz where sqx = x*x sqy = y*y sqz = z*z Theory in Programming Practice, Plaxton, Spring 2004
The where Clause • The following alternative also works pythagoras x y z = sq x + sq y == sq z where sq p = p*p Theory in Programming Practice, Plaxton, Spring 2004
Pattern Matching • Previously we defined function imply as follows imply p q = not p || q • We can instead use pattern matching to define it as imply False q = True imply True q = q Theory in Programming Practice, Plaxton, Spring 2004
Pattern Matching • We can also define the function imply as follows imply False False = True imply False True = True imply True False = False imply True True = True • Pattern matching can also involve certain sufficiently simple arithmetic expressions suc 0 = 1 suc (n+1) = (suc n)+1 Theory in Programming Practice, Plaxton, Spring 2004
Pattern Matching • Unfortunately, a definition such as the following is not permitted since multiplication is not allowed with the pattern count 2*t = count t count 2*t + 1 = (count t) + 1 Theory in Programming Practice, Plaxton, Spring 2004
Recursive Programming • Computing powers of 2 • Counting the 1s in a binary expansion • Multiplication via addition • Fibonacci numbers • Greatest common divisor Theory in Programming Practice, Plaxton, Spring 2004
Computing Powers of 2 • Here is the definition of a recursive function that computes nonnegative integer powers of two power2 0 = 1 power2 (n+1) = 2 * (power2 n) • What is the running time of power2 ? Theory in Programming Practice, Plaxton, Spring 2004
Counting the 1s in a Binary Expansion • Here is the definition of a recursive function that computes the number of 1’s in the binary representation of its argument, where we assume that the argument is a natural number count 0 = 0 count n | even n = count (n ‘div‘ 2) | odd n = count (n ‘div‘ 2) + 1 Theory in Programming Practice, Plaxton, Spring 2004
Multiplication via Addition • Here is a simple recursive algorithm for performing multiplication via addition mlt x 0 = 0 mlt x (y+1) = (mlt x y) + x • For what integer arguments does the above approach work? • What is the running time of mlt ? Theory in Programming Practice, Plaxton, Spring 2004
Multiplication via Addition: A Faster Approach • Consider the more general problem of computing x*y + z over three given arguments x , y , and z • The recursive function quickMlt , defined below, performs this task using only addition quickMlt x 0 z = z quickMlt x y z | even y = quickMlt (2 * x ) (y ‘div‘ 2) z | odd y = quickMlt (2 * x ) (y ‘div‘ 2) (x + z) • For what integer arguments does the above approach work? • What is the running time of quickMlt ? Theory in Programming Practice, Plaxton, Spring 2004
Fibonacci Numbers • Here is a recursive algorithm for computing the Fibonacci numbers fib 0 = 0 fib 1 = 1 fib (n + 2) = (fib n) + (fib (n+1)) • What is the running time of fib ? • We will see a faster algorithm in a later lecture Theory in Programming Practice, Plaxton, Spring 2004
Greatest Common Divisor • The greatest common divisor (gcd) of two positive integers is the largest positive integer that divides them both • Here is a recursive function corresponding to (the simple version of) Euclid’s gcd algorithm gcd m n | m == n = m | m > n = gcd (m - n) n | n > m = gcd m (n - m) Theory in Programming Practice, Plaxton, Spring 2004
Greatest Common Divisor: Efficient Version • Here is a more efficient version of Euclid’s gcd algorithm egcd m n | m == 0 = n | n == 0 = m | m == n = m | m > n = egcd (m ‘rem‘ n) n | n > m = egcd m (n ‘rem‘ m) • The running time of this algorithm is bounded by the number of bits in the input – An exponential improvement over the simple version in terms of worst-case running time Theory in Programming Practice, Plaxton, Spring 2004
Greatest Common Divisor: Binary Version • Here is a an efficient “binary version” of the gcd algorithm that requires only addition and bit shift operations – Note that ‘div‘ can be implemented using a right shift bgcd m n | m == n = m | (even m) && (even n) = 2 * (bgcd s t) | (even m) && (odd n) = bgcd s n | (odd m) && (even n) = bgcd m t | m > n = bgcd (m-n) n | n > m = bgcd m (n-m) where s = m ‘div‘ 2 t = n ‘div‘ 2 • The running time of this algorithm is bounded by the number of bits in the input Theory in Programming Practice, Plaxton, Spring 2004
Recommend
More recommend