Download as pdf or txt
Download as pdf or txt
You are on page 1of 74

Recursion and Debugging

A/Prof Khoo Siau Cheng


khoosc@nus.edu.sg
Recap: Recursion
def factorial(n):
ans = 1 𝑛! = 1 × 2 × 3 × ⋯ × 𝑛
i = 1
while i <= n:
ans = ans * i
i = i + 1
print(ans)

def factorialR(n): 1 if 𝑛 = 0
if n == 1: 𝑛! = ቊ
𝑛−1 !×𝑛 otherwise
return 1
else:
return factorialR(n-1) * n
Recursion
• Rules of recursion Must have a terminal
condition
def factorialR(n):
if n == 1:
return 1
else:
return factorialR(n-1) * n Must call itself
with reduced
problem size for
every layer
deeper
Making a recursive call

4
n

def factorialR(n):
if n == 1:
return 1
else:
return n * factorialR(n-1)
Making a recursive call

n
factorialR(1)
n-1 4 n
1 1
n-1
def factorialR(n):
factorialR(2) if n == 1:
def factorialR(n): return 1
if n == 1: else:
2 2 return 1 return n * factorialR(n-1)
else:
factorialR(3) return n * factorialR(n-1)

6 3
Knowledge: pass by value, variable scope , call stack
Today
• More about Recursion
– Recursion vs Iteration
– Debugging
– Briefing on Examplify
Recursion vs Iteration
Reversing a String
• What about reversing a string? Of course, we can just use string slicing

• How about we write a function for it?


Reverse String (Iterative Version 1)

i size-i-1 s[size-i-1] output


0 4 e e
1 3 d ed
2 2 c edc
3 1 b edcb
4 0 a edcba
Reverse String (Iterative Version 2)

c output
a a
b ba
c cba
d dbca
e edbca
Reversing String (Recursive Version)

• reverseStringR(‘abcde’)
• reverseStringR(‘bcde’)+’a’
• reverseStringR(‘cde’)+’b’+’a’
• reverseStringR(‘de’)+’c’+’b’+’a’
• reverseStringR(‘e’)+’d’+’c’+’b’+’a’
• reverseStringR(‘’)+’e’+’d’+’c’+’b’+’a’
• ‘’+’e’+’d’+’c’+’b’+’a’
• ‘edcba’
Taylor Series
Taylor Series

n=1 n=2 n=3


• We do not need the infinite precision
• We may just sum up to k terms
k
Computing sine by Iteration
k

• Using iteration

Python Library version of “sin()”


Computing sine by Recursion
𝑘
(−1)𝑛 2𝑛+1 𝑥3 𝑥5
sin 𝑥 ≅ 𝑠𝑖𝑛𝑅 𝑥, 𝑘 = ෍ 𝑥 =𝑥 − + − ⋯ kth
2𝑛 + 1 ! 3! 5!
𝑛=0

n=k
𝑘−1
(−1)𝑛 2𝑛+1 (−1)𝑘
𝑠𝑖𝑛𝑅 𝑥, 𝑘 = ෍ 𝑥 + 𝑥 2𝑘+1 𝑖𝑓 𝑘 ≥ 0
2𝑛 + 1 ! 2𝑘 + 1 !
𝑛=0
(−1)𝑘
= 𝑠𝑖𝑛𝑅 𝑥, 𝑘 − 1 + 𝑥 2𝑘+1 𝑖𝑓 𝑘 ≥ 0
2𝑘 + 1 !

−1
(−1)𝑛 2𝑛+1
𝑠𝑖𝑛𝑅 𝑥, 0 = ෍ 𝑥 =0 𝑖𝑓 𝑘 < 0
2𝑛 + 1 !
𝑛=0
Computing sine by Recursion
0, 𝑘<0
𝑠𝑖𝑛𝑅 𝑥, 𝑘 = ൞ (−1)𝑘 2𝑘+1
𝑠𝑖𝑛𝑅 𝑥, 𝑘 − 1 + 𝑥 , 𝑘≥0
2𝑘 + 1 !
Recursion Common Patterns

Base cases

Recursion step to reduce the problem one-by-one


Iteration Common Patterns

Accumulate element one-by-one

Initial the final answer to “nothing” at the beginning.


Accumulate and return the final answer
Iteration/Recursion Conversion

Base case The answer for previous k – 1 terms The kth term
Iteration/Recursion Conversion

Base case The answer for previous k – 1 terms The kth term
“Homework”

• The answer for all k-1 terms?


• Base case?
• Kth term?
“Homework”
• How to re-write your code with both iterative/recursion version
mentioned in this course before?
• burgerPrice()
• checkAllAlpha()
• Etc.
• The answer for all k-1 terms?
• Base case?
• Kth term?
A Truly Recursive Example
Example: Tower of Hanoi
A B C
 This classical “Towers of Hanoi” puzzle has attracted the
attention of computer scientists more than any other
puzzles.
 Invented by Edouard Lucas, a French mathematician, in 1883.
 There are 3 pegs (A, B and C) and a tower of n disks on the
first peg A, with the smallest disk on the top and the biggest
at the bottom. The purpose of the puzzle is to move the
whole tower from peg A to peg B, with the following simple
rules:
 Only one disk (the one at the top) can be moved at a time.
 A bigger disk must not rest on a smaller disk.
27
Tower of Hanoi
 We attempt to write a program to produce instructions on how to
move the disks from peg A to peg B to complete the puzzle.
 Example: A tower with 3 disks.
 Output produced by program:
Move disk from A to B A B C
Move disk from A to C
Move disk from B to C
Move disk from A to B
Move disk from C to A
Move disk from C to B
Demo: Tower of Hanoi
Move disk from A to B
28
Tower of Hanoi – The idea of Divide & Conquer

Can be interpreted as:


1. move four disks from peg A to peg C
2. move disk #5 from peg A to peg B
3. move four disks from peg C to peg B

29
Tower of Hanoi
Towers after steps 1 and 2:
A B C

Can be interpreted as:


1. move four disks from peg A to peg C
2. move disk #5 from peg A to peg B
3. move four disks from peg C to peg B

30
Tower of Hanoi
Towers after steps 1 and 2:
A B C

Can be interpreted as:


1. move four disks from peg A to peg C
2. move disk #5 from peg A to peg B
3. move four disks from peg C to peg B

31
1. move four disks from peg A to peg C
Tower of Hanoi 2. move disk #5 from peg A to peg B
3. move four disks from peg C to peg B
Towers after steps 1 and 2:
A B C

Step 3: move four disks from peg C to peg B


can be interpreted as:
3.1 move three disks from peg C to peg A
3.2 move disk 4 from peg C to peg B
3.3 move three disks from peg A to peg B
32
3.1 move three disks from peg C to peg A
Tower of Hanoi 3.2 move disk 4 from peg C to peg B
3.3 move three disks from peg A to peg B

Towers after steps 1, 2, 3.1 and 3.2:


A B C

Are you able to visualize how to


solve this using recursion?
33
3.1 move three disks from peg C to peg A
Tower of Hanoi 3.2 move disk 4 from peg C to peg B
3.3 move three disks from peg A to peg B

Towers after steps 1, 2, 3.1 and 3.2:


A B C

In short, we tried smaller cases before,


Are you able to visualize how to and they all work.
solve this using recursion? So, we trust that these bigger steps
should work,
given the same working context.
34
Tower of Hanoi
 Algorithm: source dest temp
 To move n disks from source peg to dest
peg using temp peg
if (n > 0)
move n – 1 disks from the source peg to the
temp peg using the dest peg
move disk n from the source peg to the dest
peg
move n – 1 disks from the temp peg to the
dest peg using the source peg
35
Tower of Hanoi

36
Bugs and debugging
Bugs?
• In 1947, Grace Murray Hopper
was working on the Harvard
University Mark II Aiken Relay
Calculator (a primitive
computer).

• On the 9th of September, 1947,


when the machine was
experiencing problems, an
investigation showed that there
was a moth trapped between
the points of Relay #70, in Panel
F.
Mark I
Bugs?
• The operators removed the moth and affixed it to the log. (See the
picture above.) The entry reads: "First actual case of bug being
found."
Bug from Assignment 1
W02 debug1.py
1

Fail!

3
4

Traceback (most recent call last):


1 File "<pyshell#0>", line 1, in <module>
p1(1,2)
2 File "C:\Users\dcschl\Google Drive\Courses\IT1007\Lectures\W02 debug 1.py",
line 3, in p1
b = p3(x,y)
3 File "C:\Users\dcschl\Google Drive\Courses\IT1007\Lectures\W02 debug 1.py",
line 10, in p3
return p2(a) + p2(b)
4 TypeError: p2() missing 1 required positional argument: 'w'
The IDLE Debugger
Using the IDLE Debugger
• Load in your source code
• Turn on the debugger by
The Debugger Window Pops up
Using the IDLE Debugger
• Go to your source code window to “run”
• Then the debugger will pause the program at the first line of code and
wait for you
• You can click the button “Go”
• That will make the program run
• At this point we don’t have any error
• Because by “running” the code, we just define the three functions
Using the IDLE Debugger
• Let’s execute the function in
debug mode
• In the shell, type
p1(1,2)
• Then the debugger will pause
at the first line of p1
• If you type “go” now, you will
get an error like the last time
Using the IDLE Debugger
• Go
• Clicking this will run the program until the next break point is reached. You can insert break points
in your code by right clicking and selecting Set Breakpoint. Lines that have break points set on
them will be highlighted in yellow.
• Step
• This executes the next statement. If the statement is a function call, it will enter the function and
stop at the first line.
• Over
• This executes the next statement just as Step does. But it does not enter into functions. Instead, it
finishes executing any function in the statement and stops at the next statement in the same
scope.
• Out
• This exits the current function and stops in the caller of the current function.
• After using Step to step into a function, you can use Out to quickly execute all the statements in
the function and get back out to the outer function.
• Quit: This terminates execution.
Using the IDLE Debugger
• Currently in line 1
• Click “Step” goes
to line 2
Using the IDLE Debugger
Current position

Step: Go into functions, otherwise “over”

Out: run until


Over: run until next line
the current
function ends
More Debugging (BuggyAddNum)
Turn on Debugger
• After a few steps
Using the IDLE Debugger
Another Debugger: pythontutor.com
Debugging
Humans make mistakes
You are only human
Therefore, you will make mistakes
Debugging
• Means to remove errors (“bugs”) from a program.
• After debugging, the program is not necessarily error-free.
• It just means that whatever errors remain are harder to find.
• This is especially true for large applications.
Common Types of Errors
• Omitting return statement
def fact(n):
if n == 1:
return 1
else:
n * fact(n-1) # no error msg!
• Incompatible types
x = 5
def square(x):
return x * x
x + square
• Incorrect # args
• square(3,5)
Common Types of Errors
• Syntax
def proc(100)
do_stuff()
more()
• Arithmetic error
x=3
y=0
x/y
• Undeclared variables
x=2
x+k
Common Types of Errors
• Infinite loop (from bad inputs)
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n-1)

• fact(2.1)
• fact(-1)
Common Types of Errors
• Infinite loop (from not decrementing)
• def fact_iter(n):
• counter, result = n, 1
• while counter!= 0:
• result *= counter
• return result
Common Types of Errors
• Numerical imprecision
def foo(n):
counter, result = 0,0
while counter != n:
result += counter
counter += 0.1
return result
counter never exactly equals n
• foo(5)
Common Types of Errors
• Logic
def fib(n):
if n < 2:
return n
else:
return fib(n-1) + fib(n-1)
How to debug?
• Think like a detective
• Look at the clues: error messages, variable values.
• Eliminate the impossible.
• Run the program again with different inputs.
• Does the same error occur again?
How to debug?
• Work backwards
• From current sub-problem backwards in time
• Use a debugger
• IDLE has a simple debugger
• Overkill for our class
• Trace a function
• Display variable values
Displaying variables
debug_printing = True
def debug_print(msg):
if debug_printing:
print(msg)

def foo(n):
counter, result = 0,0
while(counter != n):
debug_print(f'{counter}, {n}, {result}')
counter, result = counter + 0.1, result + counter
return result
Example
def fib(n):
debug_print(f'n:{n}')
if n < 2:
return n
else:
return fib(n-1) + fib(n-1)
Other tips
• State assumptions clearly.
def factorial(n): # n integer >= 0
if n == 0:
return 1
else:
return n * factorial(n-1)

• Test each function before you proceed to the next.


• Remember to test boundary cases
Summary
• Compound data helps us to reason at a higher conceptual level.
• Abstraction barriers separate usage of a compound data from its
implementation.
• Only functions at the interface should be used.
• We can choose between different implementations as long as
contract is fulfilled.
Debugging is an Art
Sometime Errors are Fatal
• https://www.youtube.com/watch?v=VjJgiDuHlRw

You might also like