CS790 – Introduction to Bioinformatics

powerfultennesseeBiotechnology

Oct 2, 2013 (4 years and 3 months ago)

97 views

Recursion and Iteration

Let the entertainment begin…

CS 480/680


Comparative Languages

Recursion and Iteration

2

No Iteration??


Yes, it’s true, there is no iteration in Scheme.
So, how do we achieve simple looping
constructs? Here’s a simple example from Cal
-
Tech:

Recursion and Iteration

3

Summing integers


How do I compute the sum of the first N
integers?

Recursion and Iteration

4

Decomposing the sum



Sum of first N integers =


N + N
-
1 + N
-
2 + … 1


N + ( N
-
1 + N
-
2 + … 1 )


N + [
sum of first N
-
1 integers
]

Recursion and Iteration

5

to Scheme


Sum of first N integers =

N + [
sum of first N
-
1 integers
]



Convert to Scheme (first attempt):


(define (sum
-
integers n)


(+ n (sum
-
integers (
-

n 1))))

Recursion and Iteration

6

Recursion in Scheme


(define (
sum
-
integers

n)


(+ n (
sum
-
integers

(
-

n 1))))



sum
-
integers

defined in terms of itself


This is a
recursively defined

procedure

... which is incorrect


Can you spot the error?

Recursion and Iteration

7

Almost…


What’s wrong?

(define (sum
-
integers n)

(+ n (sum
-
integers (
-

n 1))))



Gives us:



N + N
-
1 + …+ 1 + 0 +
-
1 +
-
2 + …


Recursion and Iteration

8

Debugging


Fixing the problem:


it doesn’t stop at zero!


Revised:

(define (sum
-
integers n)


(if

(= n 0)


0


(+ n (sum
-
integers (
-

n 1)))
)
)


How does this evaluate?

Recursion and Iteration

9

Warning


The substitution evaluation you are about to
see is methodical, mechanistic,


and extremely tedious.



It will not all fit on one slide.


Don't take notes
, but instead try to grasp the
overall theme of the evaluation.

Recursion and Iteration

10

Evaluating


Evaluate:
(sum
-
integers 3)


evaluate
3



3


evaluate
sum
-
integers



(lambda (n) (if …))


apply
(lambda (n)


(if (= n 0)


0


(+ n (sum
-
integers (
-

n 1)))))

to
3




(if (= 3 0) 0 (+ 3 (sum
-
integers (
-

3 1))))

Recursion and Iteration

11

Evaluating…


Evaluate:

(if (= 3 0) 0 (+ 3 (sum
-
integers (
-

3 1))))


evaluate
(= 3 0)


evaluate
3



3


evaluate
0



0


evaluate
=



=


apply
=

to
3
,
0



#f


since expression is false,


replace with false clause:


(+ 3 (sum
-
integers (
-

3 1)))


evaluate:
(+ 3 (sum
-
integers (
-

3 1)))

Recursion and Iteration

12

Evaluating…


evaluate
(+ 3 (sum
-
integers (
-

3 1)))


evaluate
3


3


evaluate
(sum
-
integers (
-

3 1))


evaluate
(
-

3 1)

...[skip steps] …


2


evaluate
sum
-
integers

(lambda (n) (if …))

Recursion and Iteration

13

Evaluating…


evaluate
(+ 3

(sum
-
integers (
-

3 1))
)


evaluate
3


3


evaluate
(sum
-
integers (
-

3 1))


evaluate
(
-

3 1)

...[skip steps] …


2


evaluate
sum
-
integers

(lambda (n) (if …))

Note: now pending (+ 3 …)

Recursion and Iteration

14

Evaluating…


apply
(lambda (n)


(if (= n 0)


0


(+ n (sum
-
integers (
-

n 1)))))

to
2



(if (= 2 0) 0 (+ 2 (sum
-
integers (
-

2 1)))


Pending (+ 3 …)

Recursion and Iteration

15

Evaluating…


evaluate:

(if (= 2 0) 0 (+ 2 (sum
-
integers (
-

2 1)))


evaluate
(= 2 0)

… [skip steps] …


#f


since expression is false,


replace with false clause


(+ 2 (sum
-
integers (
-

2 1)))


evaluate:
(+ 2 (sum
-
integers (
-

2 1)))


Pending (+ 3 …)

Recursion and Iteration

16

Evaluating…


evaluate (+ 2 (sum
-
integers (
-

2 1)))


evaluate 2


2


evaluate (sum
-
integers (
-

2 1))


evaluate
(
-

2 1)

...[skip steps] …


1


evaluate
sum
-
integers

(lambda (n) (if …))


apply
(lambda (n) …)

to
1


(if (= 1 0) 0 (+ 1 (sum
-
integers (
-

1 1))))


evaluate
(= 1 0)

...[skip steps] …



#f

Pending (+ 3 …)

Note: pending (+ 3 (+ 2 …))

Recursion and Iteration

17

Evaluating…


Evaluate
(+ 1 (sum
-
integers (
-

1 1)))


evaluate

1


1


evaluate

(sum
-
integers (
-

1 1))


evaluate
(
-

1 1)

...[skip steps] …


0


evaluate
sum
-
integers

(lambda (n) (if …))


apply
(lambda (n) …)

to
0


(if (= 0 0) 0 (+ 1 (sum
-
integers (
-

0 1))))


evaluate
(= 0 0)



#t


result:
0

Pending (+ 3 (+ 2 …))

Note: pending (+ 3 (+ 2 (+ 1 0)))

Recursion and Iteration

18

Evaluating

Back to pending:


Know
(sum
-
integers (
-

1 1))



0

[prev slide]


Evaluate
(+ 1 (sum
-
integers (
-

1 1)))


evaluate
1



1


evaluate
(sum
-
integers (
-

1 1))



0


evaluate
+



+


apply
+

to
1
,
0



1

Pending (+ 3 (+ 2 (+ 1 0)))

Note: pending (+ 3 (+ 2
1
))

Recursion and Iteration

19

Evaluating

Back to pending:


Know
(sum
-
integers (
-

2 1))



1
[prev slide]


Evaluate
(+ 2 (sum
-
integers (
-

2 1)))


evaluate
2


2


evaluate
(sum
-
integer (
-

2 1))


1



evaluate
+


+


apply
+

to
2
,
1


3

Pending (+ 3 (+ 2 1))

Note: pending (+ 3
3
)

Recursion and Iteration

20

Evaluating

Finish pending:


(+ 3 3)


… final result:
6

Recursion and Iteration

21

…Yeah!!! …

Substitution model…


…works fine for recursion.


Recursive calls are well
-
defined.


Careful application of model shows us what
they mean and how they work.

Recursion and Iteration

22

Recursive Functions


Since there are no iterative constructs in
Scheme, most of the power comes from writing
recursive functions


This is fairly straightforward
if

you want the
procedure to be global:

(define factorial


(lambda (n)


(if (= n 0) 1


(* n (factorial (
-

n 1))))))


(factorial 5)
»

120

Recursion and Iteration

23

The trick


Must find the structure of the problem:


How can I break it down into sub
-
problems?


What are the base cases?

Recursion and Iteration

24

List Processing


Suppose I want to search a list for the max
value?


A standard list:





What is the base case?


What state do I need to maintain?

3

7

4

1

lst

Recursion and Iteration

25

More list processing


How about a list of X
-
Y pairs?


What would the list look like?


Given X, find Y:


Base case


State


Helper procedures?

3

7

4

1

lst

5

2

3

9

Recursion and Iteration

26

Using Recursion


Write avg.scheme


See list
-
position.scheme


See count_atoms.scheme


See printtype.scheme


Recursion and Iteration

27

Mutual Recursion


Note: Scheme has built
-
in primitives
even?

and
odd?

(define is
-
even?


(lambda (n)


(if (= n 0) #t


(is
-
odd? (
-

n 1)))))


(define is
-
odd?


(lambda (n)


(if (= n 0) #f


(is
-
even? (
-

n 1)))))

Recursion and Iteration

28

Recursion and Local Definitions


Recursion gets more difficult when you want
the functions to be local in scope:

(let ((local
-
even? (lambda (n)


(if (= n 0) #t


(local
-
odd? (
-

n 1)))))


(local
-
odd? (lambda (n)


(if (= n 0) #f


(local
-
even? (
-

n 1))))))


(list (local
-
even? 23) (local
-
odd? 23)))

local
-
odd? is not yet defined

Can we fix this with let* ?

Recursion and Iteration

29

letrec


letrec


the variables introduced by letrec are
available in both the body and the initializations


Custom made for local recursive definitions

(letrec ((local
-
even? (lambda (n)


(if (= n 0) #t


(local
-
odd? (
-

n 1)))))


(local
-
odd? (lambda (n)


(if (= n 0) #f


(local
-
even? (
-

n 1))))))


(list (local
-
even? 23) (local
-
odd? 23)))

Recursion and Iteration

30

Recursion for loops


Instead of a for loop, this letrec uses a recursive
procedure on the variable
i
, which starts at 10 in
the procedure call.

(letrec ((countdown (lambda (i)


(if (= i 0) 'liftoff


(begin


(display i)


(newline)


(countdown (
-

i 1)))))))


(countdown 10))

Recursion and Iteration

31

Named
let


This is the same as the previous definition, but
written more compactly


Named
let

is useful for
defining and running

loops

((let countdown ((i 10))


(if (= i 0) 'liftoff


(begin


(display i)


(newline)


(countdown (
-

i 1)))))

Recursion and Iteration

32

Recursion and Stack Frames


What happens when we
call a recursive function?


Consider the following
Fibonacci function:

int fib(int N) {


int prev, pprev;



if

(N == 1) {



return 0;


}


else if

(N == 2) {


return 1;


}


else

{


prev = fib(N
-
1);


pprev = fib(N
-
2);


return prev + pprev;


}

}

Recursion and Iteration

33

Stack Frames


Each call to Fib
produces a new
stack frame


1000 recursive calls
= 1000 stack frames!


Inefficient

relative to
a
for

loop

N

prev

pprev

N

prev

pprev

Recursion and Iteration

34


How can we fix this inefficiency?



Here’s an answer from the Cal
-
Tech slides:

Recursion and Iteration

35

Example


Recall from last time:

(define (sum
-
integers n)


(if (= n 0)


0


(+ n (sum
-
integers (
-

n 1)))))


Recursion and Iteration

36

Revisited (summarizing steps)


Evaluate:
(sum
-
integers 3)


(if (= 3 0) 0 (+ 3 (sum
-
integers (
-

3 1))))


(if #f 0 (+ 3 (sum
-
integers (
-

3 1))))


(+ 3 (sum
-
integers (
-

3 1)))


(+ 3 (sum
-
integers 2))


(+ 3 (if (= 2 0) 0 (+ 2 (sum
-
integers (
-

2 1)))))


(+ 3 (+ 2 (sum
-
integers 1)))


(+ 3 (+ 2 (if (= 1 0) 0 (+ 1 (sum
-
integer (
-

1 1))))))

Recursion and Iteration

37

Revisited (summarizing steps)


(+ 3 (+ 2 (+ 1 (sum
-
integers 0))))


(+ 3 (+ 2 (+ 1 (if (= 0 0) 0 …))))


(+ 3 (+ 2 (+ 1 (if #t 0 …))))


(+ 3 (+ 2 ( + 1 0)))





6


Recursion and Iteration

38

Evolution of computation


(sum
-
integers 3)


(+ 3 (sum
-
integers 2))


(+ 3 (+ 2 (sum
-
integers 1)))


(+ 3 (+ 2 (+ 1 (sum
-
integers 0))))


(+ 3 (+ 2 (+ 1 0)))

Recursion and Iteration

39

What can we say about the
computation?

On input
N
:


How many calls to
sum
-
integers
?

N+1


How much work per call?

(sum
-
integers 3)

(+ 3 (sum
-
integers 2))

(+ 3 (+ 2 (sum
-
integers 1)))

(+ 3 (+ 2 (+ 1 (sum
-
integers 0))))

(+ 3 (+ 2 (+ 1 0)))


Recursion and Iteration

40

Linear recursive processes


This is what we call a
linear recursive

process


Makes
linear

# of calls


i.e.

proportional to
N


Keeps a chain of
deferred

operations linear in size
w.r.t. input


i.e.

proportional to
N

(sum
-
integers 3)

(+ 3 (sum
-
integers 2))

(+ 3 (+ 2 (sum
-
integers 1)))

(+ 3 (+ 2 (+ 1 (sum
-
integers 0))))

(+ 3 (+ 2 (+ 1 0)))



time = C
1

+ N*C
2

Recursion and Iteration

41

Another strategy

To sum integers…

add up as we go along:


1 2 3 4 5 6 7 …


1 1+2 1+2+3 1+2+3+4 ...


1 3 6 10 15 21 28 …


Recursion and Iteration

42

Alternate definition

(define (sum
-
int n)


(sum
-
iter 0 n 0))

;; start at 0, sum is 0

(define (sum
-
iter current max sum)


(if (> current max)


sum


(sum
-
iter (+ 1 current)


max


(+ current sum))))

Recursion and Iteration

43

Evaluation of
sum
-
int

(define (sum
-
int n)


(sum
-
iter 0 n 0))

(define (sum
-
iter current


max


sum)


(if (> current max) sum


(sum
-
iter (+ 1 current)


max


(+ current


sum))))


(sum
-
int 3)


(sum
-
iter 0 3
0
)

Recursion and Iteration

44

Evaluation of
sum
-
int

(define (sum
-
int n)


(sum
-
iter 0 n 0))

(define (sum
-
iter current


max


sum)


(if (> current max) sum


(sum
-
iter (+ 1 current)


max


(+ current


sum))))


(sum
-
int 3)


(sum
-
iter
0

3
0
)


(if (> 0 3) … )


(sum
-
iter 1 3
0
)

Recursion and Iteration

45

Evaluation of
sum
-
int

(define (sum
-
int n)


(sum
-
iter 0 n 0))

(define (sum
-
iter current


max


sum)


(if (> current max) sum


(sum
-
iter (+ 1 current)


max


(+ current


sum))))


(sum
-
int 3)


(sum
-
iter 0 3 0)


(if (> 0 3) … )


(sum
-
iter
1

3
0
)


(if (> 1 3) … )


(sum
-
iter 2 3
1
)

Recursion and Iteration

46

Evaluation of
sum
-
int

(define (sum
-
int n)


(sum
-
iter 0 n 0))

(define (sum
-
iter current


max


sum)


(if (> current max) sum


(sum
-
iter (+ 1 current)


max


(+ current


sum))))


(sum
-
int 3)


(sum
-
iter 0 3 0)


(if (> 0 3) … )


(sum
-
iter 1 3 0)


(if (> 1 3) … )


(sum
-
iter
2

3
1
)


(if (> 2 3) … )


(sum
-
iter 3 3
3
)

Recursion and Iteration

47

Evaluation of
sum
-
int

(define (sum
-
int n)


(sum
-
iter 0 n 0))

(define (sum
-
iter current


max


sum)


(if (> current max) sum


(sum
-
iter (+ 1 current)


max


(+ current


sum))))


(sum
-
int 3)


(sum
-
iter 0 3 0)


(if (> 0 3) … )


(sum
-
iter 1 3 0)


(if (> 1 3) … )


(sum
-
iter 2 3 1)


(if (> 2 3) … )


(sum
-
iter
3

3
3
)


(if (> 3 3) … )


(sum
-
iter 4 3
6
)

Recursion and Iteration

48

Evaluation of
sum
-
int

(define (sum
-
int n)


(sum
-
iter 0 n 0))

(define (sum
-
iter current


max


sum)


(if (> current max) sum


(sum
-
iter (+ 1 current)


max


(+ current


sum))))


(sum
-
int 3)


(sum
-
iter 0 3 0)


(if (> 0 3) … )


(sum
-
iter 1 3 0)


(if (> 1 3) … )


(sum
-
iter 2 3 1)


(if (> 2 3) … )


(sum
-
iter 3 3 3)


(if (> 3 3) … )


(sum
-
iter 4 3 6)


(if (> 4 3) … )


6

Recursion and Iteration

49

What can we say about the
computation?

on input
N
:


How many calls to
sum
-
iter
?

N+2


How much work per call?



(sum
-
int 3)


(sum
-
iter 0 3 0)


(sum
-
iter 1 3 0)


(sum
-
iter 2 3 1)


(sum
-
iter 3 3 3)


(sum
-
iter 4 3 6)


6


Recursion and Iteration

50

What can we say about the
computation?

on input
N
:


How many calls to
sum
-
iter
?

N+2


How much work per call?

constant



one comparison, one if,
two additions, one call


How many deferred
operations?

none


(sum
-
int 3)


(sum
-
iter 0 3 0)


(sum
-
iter 1 3 0)


(sum
-
iter 2 3 1)


(sum
-
iter 3 3 3)


(sum
-
iter 4 3 6)


6


Recursion and Iteration

51

What’s different about these
computations?

(sum
-
integers 3)

(+ 3 (sum
-
integers 2))

(+ 3 (+ 2 (sum
-
integers 1)))

(+ 3 (+ 2 (+ 1 (sum
-
integers 0))))

(+ 3 (+ 2 (+ 1 0)))

(sum
-
int 3)

(sum
-
iter 0 3 0)

(sum
-
iter 1 3 0)

(sum
-
iter 2 3 1)

(sum
-
iter 3 3 3)

(sum
-
iter 4 3 6)

Old

Ne
w

Recursion and Iteration

52

Linear iterative processes


This is what we call a
linear iterative

process


Makes linear # calls


State

of computation kept
in a constant # of
state variables


state does not grow with
problem size


requires a constant amount
of storage space


(sum
-
int 3)


(sum
-
iter 0 3 0)


(sum
-
iter 1 3 0)


(sum
-
iter 2 3 1)


(sum
-
iter 3 3 3)


(sum
-
iter 4 3 6)


6


time = C
1

+ N*C
2

Recursion and Iteration

53

Linear recursive vs linear iterative


Both require computational
time

which is
linear in size of input


L.R. process requires
space

that is also
linear in size of input


why?


L.I. process requires
constant

space


not

proportional to size of input


more space
-
efficient than L.R. process

Recursion and Iteration

54

Linear recursive vs linear iterative


Why not always use linear iterative
instead of linear recursive algorithm?


often the case in practice


Recursive algorithm sometimes much
easier to write


and to prove correct


Sometimes space efficiency not the
limiting factor

Recursion and Iteration

55

Tail
-
call elimination


Countdown uses only
tail
-
recursion


Each call to countdown either does not call itself
again, or does it as the
very last thing done


Scheme recognizes tail recursion and converts
it to an iterative (loop) construct internally

((let countdown ((i 10))


(if (= i 0) 'liftoff


(begin


(display i)


(newline)


(countdown (
-

i 1)))))

Recursion and Iteration

56

Mapping a procedure across a list

(map add2 '(1 2 3))

»

(3 4 5)


(map cons '(1 2 3) '(10 20 30))

»

((1 . 10) (2 . 20) (3 . 30))


(map + '(1 2 3) '(10 20 30))

»

(11 22 33)


Recursion and Iteration

57

Exercises


Include error checking into avg.scheme


Divide by zero error


Non
-
numeric list items


Write a scheme function that accepts a single
numeric argument and returns all numbers less
than 10 that the argument is divisible by


Factor.scheme


described in class


Write a scheme function that returns the first n
Fibonacci numbers as a list (n is an argument)