40
Functional Programming Recursion H. Turgut Uyar 2013-2015

Functional Programming - Recursion

Embed Size (px)

DESCRIPTION

Recursion, tail recursion.

Citation preview

Page 1: Functional Programming - Recursion

Functional ProgrammingRecursion

H. Turgut Uyar

2013-2015

Page 2: Functional Programming - Recursion

License

© 2013-2015 H. Turgut Uyar

You are free to:

Share – copy and redistribute the material in any medium or format

Adapt – remix, transform, and build upon the material

Under the following terms:

Attribution – You must give appropriate credit, provide a link to the license, andindicate if changes were made.

NonCommercial – You may not use the material for commercial purposes.

ShareAlike – If you remix, transform, or build upon the material, you mustdistribute your contributions under the same license as the original.

For more information:https://creativecommons.org/licenses/by-nc-sa/4.0/

Read the full license:

https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode

Page 3: Functional Programming - Recursion

Topics

1 FunctionsInfix - PrefixGuardsErrors

2 RecursionPrimitive RecursionTail RecursionTree RecursionExamples

Page 4: Functional Programming - Recursion

Topics

1 FunctionsInfix - PrefixGuardsErrors

2 RecursionPrimitive RecursionTail RecursionTree RecursionExamples

Page 5: Functional Programming - Recursion

Infix - Prefix

functions infix when in backquotes

mod n 2n ‘mod‘ 2

operators prefix when in parentheses

6 * 7(*) 6 7

Page 6: Functional Programming - Recursion

Infix - Prefix

functions infix when in backquotes

mod n 2n ‘mod‘ 2

operators prefix when in parentheses

6 * 7(*) 6 7

Page 7: Functional Programming - Recursion

Topics

1 FunctionsInfix - PrefixGuardsErrors

2 RecursionPrimitive RecursionTail RecursionTree RecursionExamples

Page 8: Functional Programming - Recursion

Guards

writing conditional expressions can become complicated

guards: predicates to check cases

n :: t1 -> t2 -> ... -> tk -> tn x1 x2 ... xk| p1 = e1| p2 = e2...

| otherwise = e

function result is the expression for the first satisfied predicate

Page 9: Functional Programming - Recursion

Guard Example

maximum of three integers

maxThree :: Integer -> Integer -> Integer -> IntegermaxThree x y z| x>=y && x>=z = x| y>=z = y| otherwise = z

Page 10: Functional Programming - Recursion

Topics

1 FunctionsInfix - PrefixGuardsErrors

2 RecursionPrimitive RecursionTail RecursionTree RecursionExamples

Page 11: Functional Programming - Recursion

Errors

errors can be reported using error

doesn’t change the type signature

example: reciprocal (multiplicative inverse)

reciprocal :: Float -> Floatreciprocal x| x == 0 = error "division by zero"| otherwise = 1.0 / x

Page 12: Functional Programming - Recursion

Topics

1 FunctionsInfix - PrefixGuardsErrors

2 RecursionPrimitive RecursionTail RecursionTree RecursionExamples

Page 13: Functional Programming - Recursion

Recursion Examples

greatest common divisor

gcd :: Integer -> Integer -> Integergcd x y = if y == 0 then x else gcd y (x ‘mod‘ y)

factorial

fac :: Integer -> Integerfac n| n < 0 = error "negative parameter"| n == 0 = 1| otherwise = n * fac (n - 1)

Page 14: Functional Programming - Recursion

Recursion Examples

greatest common divisor

gcd :: Integer -> Integer -> Integergcd x y = if y == 0 then x else gcd y (x ‘mod‘ y)

factorial

fac :: Integer -> Integerfac n| n < 0 = error "negative parameter"| n == 0 = 1| otherwise = n * fac (n - 1)

Page 15: Functional Programming - Recursion

Stack Frame Example

gcd x y = if y == 0 then x else gcd y (x ‘mod‘ y)

gcd 9702 945~> gcd 945 252

~> gcd 252 189~> gcd 189 63

~> gcd 63 0~> 63

~> 63~> 63

~> 63~> 63

Page 16: Functional Programming - Recursion

Stack Frame Example

fac n| n < 0 = error "negative parameter"| n == 0 = 1| otherwise = n * fac (n - 1)

fac 4~> 4 * fac 3

~> 3 * fac 2~> 2 * fac 1

~> 1 * fac 0~> 1

~> 1~> 2

~> 6~> 24

Page 17: Functional Programming - Recursion

Topics

1 FunctionsInfix - PrefixGuardsErrors

2 RecursionPrimitive RecursionTail RecursionTree RecursionExamples

Page 18: Functional Programming - Recursion

Tail Recursion

if the result of the recursive call is also the result of the caller,then the function is said to be tail recursive

the recursive function call is the last action:nothing left for the caller to do

no need to keep the stack frame around:reuse the frame of the caller

Page 19: Functional Programming - Recursion

Tail Recursion

if the result of the recursive call is also the result of the caller,then the function is said to be tail recursive

the recursive function call is the last action:nothing left for the caller to do

no need to keep the stack frame around:reuse the frame of the caller

Page 20: Functional Programming - Recursion

Stack Frame Example

gcd x y = if y == 0 then x else gcd y (x ‘mod‘ y)

gcd 9702 945~> gcd 945 252~> gcd 252 189~> gcd 189 63~> gcd 63 0~> 63

Page 21: Functional Programming - Recursion

Tail Recursion

to rearrange a function to be tail recursive:

define a helper function that takes an accumulator

base case: return accumulator

recursive case: make recursive call with new accumulator value

Page 22: Functional Programming - Recursion

Tail Recursion Example

tail recursive factorial

facIter :: Integer -> Integer -> IntegerfacIter acc n| n < 0 = error "negative parameter"| n == 0 = acc| otherwise = facIter (acc * n) (n - 1)

fac :: Integer -> Integerfac n = facIter 1 n

Page 23: Functional Programming - Recursion

Stack Frame Example

facIter acc n| n < 0 = error "negative parameter"| n == 0 = acc| otherwise = facIter (acc * n) (n - 1)

fac 4~> facIter 1 4

~> facIter 4 3~> facIter 12 2~> facIter 24 1~> facIter 24 0~> 24

~> 24

Page 24: Functional Programming - Recursion

Tail Recursion Example

helper function can be local

negativity check only once

fac :: Integer -> Integerfac n| n < 0 = error "negative parameter"| otherwise = facIter 1 n

wherefacIter :: Integer -> Integer -> IntegerfacIter acc n’| n’ == 0 = acc| otherwise = facIter (acc * n’) (n’ - 1)

Page 25: Functional Programming - Recursion

Topics

1 FunctionsInfix - PrefixGuardsErrors

2 RecursionPrimitive RecursionTail RecursionTree RecursionExamples

Page 26: Functional Programming - Recursion

Tree Recursion

Fibonacci sequence

fibn =

1 if n = 1

1 if n = 2

fibn−2 + fibn−1 if n > 2

fib :: Integer -> Integerfib n| n == 1 = 1| n == 2 = 1| otherwise = fib (n - 2) + fib (n - 1)

Page 27: Functional Programming - Recursion

Stack Frame Example

Page 28: Functional Programming - Recursion

Topics

1 FunctionsInfix - PrefixGuardsErrors

2 RecursionPrimitive RecursionTail RecursionTree RecursionExamples

Page 29: Functional Programming - Recursion

Exponentiation

pow :: Integer -> Integer -> Integerpow x y| y == 0 = 1| otherwise = x * pow x (y - 1)

use the following definition:

xy =

1 if y = 0

(xy/2)2

if y is even

x · xy−1 if y is odd

Page 30: Functional Programming - Recursion

Exponentiation

pow :: Integer -> Integer -> Integerpow x y| y == 0 = 1| otherwise = x * pow x (y - 1)

use the following definition:

xy =

1 if y = 0

(xy/2)2

if y is even

x · xy−1 if y is odd

Page 31: Functional Programming - Recursion

Fast Exponentiation

pow :: Integer -> Integer -> Integerpow x y| y == 0 = 1| even y = sqr (pow x (y ‘div‘ 2))| otherwise = x * pow x (y - 1)wheresqr :: Integer -> Integersqr n = n * n

Page 32: Functional Programming - Recursion

Square Roots with Newton’s Method

start with an initial guess y (say y = 1)

repeatedly improve the guess by taking the mean of y and x/y

until the guess is good enough (√

x ·√

x = x)

example:√

2

y x/y next guess1 2 / 1 = 2 1.51.5 2 / 1.5 = 1.333 1.41671.4167 2 / 1.4167 = 1.4118 1.41421.4142 ... ...

Page 33: Functional Programming - Recursion

Square Roots with Newton’s Method

newton :: Float -> Float -> Floatnewton guess x| isGoodEnough guess x = guess| otherwise = newton (improve guess x) x

isGoodEnough :: Float -> Float -> BoolisGoodEnough guess x = abs (guess*guess - x) < 0.001

improve :: Float -> Float -> Floatimprove guess x = (guess + x/guess) / 2.0

sqrt :: Float -> Floatsqrt x = newton 1.0 x

Page 34: Functional Programming - Recursion

Square Roots with Newton’s Method

newton :: Float -> Float -> Floatnewton guess x| isGoodEnough guess x = guess| otherwise = newton (improve guess x) x

isGoodEnough :: Float -> Float -> BoolisGoodEnough guess x = abs (guess*guess - x) < 0.001

improve :: Float -> Float -> Floatimprove guess x = (guess + x/guess) / 2.0

sqrt :: Float -> Floatsqrt x = newton 1.0 x

Page 35: Functional Programming - Recursion

Square Roots with Newton’s Method

newton :: Float -> Float -> Floatnewton guess x| isGoodEnough guess x = guess| otherwise = newton (improve guess x) x

isGoodEnough :: Float -> Float -> BoolisGoodEnough guess x = abs (guess*guess - x) < 0.001

improve :: Float -> Float -> Floatimprove guess x = (guess + x/guess) / 2.0

sqrt :: Float -> Floatsqrt x = newton 1.0 x

Page 36: Functional Programming - Recursion

Square Roots with Newton’s Method

newton :: Float -> Float -> Floatnewton guess x| isGoodEnough guess x = guess| otherwise = newton (improve guess x) x

isGoodEnough :: Float -> Float -> BoolisGoodEnough guess x = abs (guess*guess - x) < 0.001

improve :: Float -> Float -> Floatimprove guess x = (guess + x/guess) / 2.0

sqrt :: Float -> Floatsqrt x = newton 1.0 x

Page 37: Functional Programming - Recursion

Square Roots with Newton’s Method

sqrt :: Float -> Floatsqrt x = newton 1.0 xwherenewton :: Float -> Float -> Floatnewton guess x’| isGoodEnough guess x’ = guess| otherwise = newton (improve guess x’) x’

isGoodEnough :: Float -> Float -> BoolisGoodEnough guess x’ =

abs (guess*guess - x’) < 0.001

improve :: Float -> Float -> Floatimprove guess x’ = (guess + x’/guess) / 2.0

Page 38: Functional Programming - Recursion

Square Roots with Newton’s Method

doesn’t work with too small and too large numbers (why?)

isGoodEnough guess x’ =(abs (guess*guess - x’)) / x’ < 0.001

Page 39: Functional Programming - Recursion

Square Roots with Newton’s Method

no need to pass x around, it’s already in scope

sqrt x = newton 1.0wherenewton :: Float -> Floatnewton guess| isGoodEnough guess = guess| otherwise = newton (improve guess)

isGoodEnough :: Float -> BoolisGoodEnough guess =

(abs (guess*guess - x)) / x < 0.001

improve :: Float -> Floatimprove guess = (guess + x/guess) / 2.0

Page 40: Functional Programming - Recursion

References

Required Reading: ThompsonChapter 3: Basic types and definitions

Chapter 4: Designing and writing programs