Cyriak: conceptually disruptive recursion… Baaa
Welcome to IST338… Be sure to watch your head!
We're computationally complete! IST338: now recursing… putting Python to work! Hw 2 – due Friday 2/20 ~ as usual pr1 lab – Turtle! pr2 – Monte Carlo simulation What's next? pr0 reading – Watson! Or re-cursing, depending on your feelings about recursion! pr3+4 – extra-credit probs! & adding building-blocks
Recursive design works… def mylen(s): """ input: any string, s output: the number of characters in s """ if s == '': return 0 else: return 1 + mylen(s[1:]) There's not much len left here! … by solving a smaller version of the same problem!
mylen('cs5') Behind the curtain: how recursion works... def mylen(s): if s == '': return 0 else: return 1 + mylen(s[1:]) 1 + mylen('s5') mylen('5') mylen('')
mymax( [1,4,3,42,-100,7] ) def mymax(L): if len(L) == 1: return L[0] elif L[0] < L[1]: return mymax( L[1:] ) else: return mymax( L[0:1]+L[2:] ) mymax( [4,3,42,-100,7] ) mymax( [4,42,-100,7] ) mymax( [42,-100,7] ) mymax( [42,7] ) mymax( [42] ) 42 base case drop 1st drop 2nd
Picture it! power(2,5) -> 2*2 power(2,0) -> == *2* p 2*2* 2 4 power(2,p) -> 2*power(…,…) Do you see the call to power!? def power(b,p): """ returns b to the p power Use recursion, not ** Inputs: int b, int p: the base and the power """ What should this be? power(b,p) ->
Picture it! power(2,5) -> 2*2 power(2,0) -> == *2* p 2*2* 2 4 power(2,p) -> 2*power(…,…) Do you see the call to power!? Try it! def power(b,p): Handle negative p values w/elif. """ returns b to the p power Use recursion, not ** Inputs: int b, int p: the base and the power """ Want more power? E.g., power(5,-1) == 0.2 if : Base case test p == 0 return else: return Base case Recursive case What should this be? power(b,p) ->
power( 2, 5 ) 2* power( 2, 4 ) 2* 2* power( 2, 3 ) 2* 2* 2* power( 2, 2 ) 2* 2* 2* 2* power( 2, 1 ) 2* 2* 2* 2* 2* power( 2, 0 ) 2* 2* 2* 2* 2* 1 32
Picture it! sajak('') 0 NOT a space – this is no characters at all. This is the empty string – and it has 0 vowels! def sajak(s): """ returns the number of vowels in the input string, s """ '' sajak('okay') 1+sajak( ) 'okay' sajak('what') 0+sajak( ) 'what' starts with a vowel – count that vowel and delegate the rest to recursion starts with a consonant – so skip it and delegate the rest!
Picture it! sajak('') 0 NOT a space – this is no characters at all. This is the empty string – and it has 0 vowels! def sajak(s): What 7-letter English word w maximizes sajak(w) ? """ returns the number of vowels in the input string, s """ Want more Pat? if s == '' : elif else: return Base case test '' sajak('okay') 1+sajak( ) 'okay' sajak('what') 0+sajak( ) 'what' starts with a vowel – count that vowel and delegate the rest to recursion Try it! starts with a consonant – so skip it and delegate the rest!
sajak( 'eerier' ) 1+ sajak( 'erier' ) sajak( 'rier' ) sajak( 'ier' ) sajak( 'er' ) sajak( 'r' ) sajak( '' )
def power(b,p): """ inputs: base b and power p (an int) implements: b**p """ if p == 0: return 1.0 elif p < 0: return ____________________ else: return b * power(b,p-1) Recursion is power!
# of vowels in s sajak(s): Base case? When there are no letters, there are ZERO vowels if s[0] is NOT a vowel, the answer is Rec. step? Look at the initial character. if s[0] is a vowel, the answer is DESIGNDESIGN sajak( s[1:] ) 1 + sajak( s[1:] )
def sajak(s): if s == '': return 0 elif s[0]=='a' or s[0]=='e' or… but how to check for vowels? Is s[0] a vowel?
Python is… in >>> 'i' in 'team' False >>> 'cs' in 'physics' True >>> 42 in [41,42,43] True >>> 42 in [[42], '42'] False I guess Python's the in thing >>> 'i' in 'alien' True >>> 3*'i' in 'alien' False
def sajak(s): if len(s) == 0: return 0 elif s[0] in 'aeiou': return 1 + sajak(s[1:]) else: return 0 + sajak(s[1:]) if s[0] is NOT a vowel, the answer is just the number of vowels in the rest of s if s[0] IS a vowel, the answer is 1 + the # of vowels in the rest of s Base Case Recursive Cases let's input 'eerier' for s
The key to understanding recursion is, first, to understand recursion. - a former student LAC all week! Good luck with Homework #1 It's the eeriest!
Three random asides… import random choice( L ) choice(['cmc','scripps','pitzer','pomona']) chooses 1 element from the sequence L allows use of dir(random) and help(random) How could you get a random int from 0 to 99 inclusive? from random import * all random functions are now available! choice('mudd') uniform(low,hi) chooses a random float from low to hi floats have 16 places of precision Aargh – so close! range(1,5)[1,2,3,4]
A random function… print the guesses ? return the number of guesses ? from random import * def guess( hidden ): """ tries to guess our "hidden" # """ compguess = choice( range(100) ) if compguess == hidden: # at last! print 'I got it!' else: guess( hidden ) Suspicious? I am! slow down… investigate expected # of guesses?!?? Remember, this is [0,1,…,99]
Empirical Hypothesis Testing… a.k.a. How many guesses do we expect in order to find the correct number?
from random import * import time def guess( hidden ): """ guessing game """ compguess = choice( range(100) ) # print 'I choose', compguess # time.sleep(0.05) if compguess == hidden: # at last! # print 'I got it!' return 1 else: return 1 + guess( hidden ) Recursive guess-counting
A few random thoughts… choice( range(1,5)+[4,2,4,2] ) uniform( -20.5, 0.5 ) What are the chances this returns a 2 ? from random import * What're the chances of this being > 0? choice( '1,2,3,4' ) choice( ['1,2,3,4'] ) choice( '[1,2,3,4]' ) choice( [1,2,3,2] ) choice( range(5) ) What's the most likely return value here? Quiz Name(s): What's the most likely return value here? What are the chances of this returning a 4 ? Is this more likely to be even or odd (or same)? Careful! Extra! choice( 0,1,2,3,4 ) choice[ range(5) ] choice( [range(5)] ) Which two of these are syntax errors? What does the third, "correct" one do? and how likely are all these?
[ [0,1,2,3,4] ] choice( range(1,5)+[4,2,4,2] ) uniform( -20.5, 0.5 ) What are the chances this returns a 2 ? What're the chances of this being > 0? choice( '1,2,3,4' ) choice( ['1,2,3,4'] ) choice( '[1,2,3,4]' ) choice( [1,2,3,2] ) choice( range(5) ) What's the most likely return value here? What are the chances of this returning a 4 ? Is this more likely to be even or odd (or same)? 1/42 choice( 0,1,2,3,4 ) choice[ range(5) ] choice( [range(5)] ) and how likely are all these? 2/4 or 50% 3/8 ',' 3/7 '1,2,3,4' 1/1 ',' 3/9 even 3/5 [0,1,2,3,4] syntax error correct: always returns [0,1,2,3,4] syntax error 1/1 chance [1,2,3,4,4,2,4,2] Data is in black. Probabilities are in blue.
The two Monte Carlos Monte Carlo casino, Monaco Insights via random trials Monte Carlo methods, Math/CS and their denizens…
Insights via random trials Monte Carlo casino, Monaco Monte Carlo methods, Math/CS Stanislaw Ulam (Los Alamos badge) Bond, James Bond The two Monte Carlos and their denizens… Ulam, Stan Ulam
Monte Carlo in action def countDoubles( N ): """ input: the # of dice rolls to make output: the # of doubles seen """ if N == 0: return 0 # zero rolls, zero doubles… else: d1 = choice( [1,2,3,4,5,6] ) d2 = choice( range(1,7) ) if d1 != d2: return 0+countDoubles( N-1 ) # don't count it else: return 1+countDoubles( N-1 ) # COUNT IT! two dice from 1-6 inclusive where and how is the check for doubles? N is the total number of rolls How many doubles will you get in N rolls of 2 dice?
Empirical Hypothesis Testing… a.k.a. How many guesses do we expect in order to find the correct number?
Empirical Hypothesis Testing… a.k.a. Run it a zillion times! # this line runs guess(42) 1000 times! LC = [ guess(42) for x in range(1000) ] # Let's look at the first 10 of them: print LC[0:10] # Let's find the average: print "av. guesses:", sum(LC)*1.0/len(LC) Hah! Now I see why they told me I'd be making a zillion euros!
Empirical Hypothesis Testing… a.k.a. How likely are we to roll doubles on two six-sided dice? Hah! Now I see why they told me I'd be making a zillion euros!
Zillion-times testing! # this runs the doubles-counter 600 times… cd( 600 ) # Run _that_ 100 times (60,000 rolls total!) DBLS = [ cd(600) for x in range(100) ] # Look at the first 10 of these print DBLS[0:10] # Let's find the average: print "av. dbls/600:", sum(DBLS)*1.0/len(DBLS) needed a less continental name…
On balance? or maybe lighter is better?
Data Functions …together [8,9,10] sq( ) [64,81,100] [ sq(x) for x in [8,9,10] ]
List Comprehensions >>> [ 2*x for x in [0,1,2,3,4,5] ] What's the syntax saying here? List Comprehenion result [0, 2, 4, 6, 8, 10]
List Comprehensions >>> [ 2*x for x in [0,1,2,3,4,5] ] [0, 2, 4, 6, 8, 10] output input The same as map, only better! this "runner" variable can have any name... x takes on each value and 2*x is output for each one
List Comprehensions >>> [ 10*x for x in [0,1,2,3,4,5] if x%2==0] result >>> [ y*21 for y in range(0,3) ] LC result >>> [ s[1] for s in ["hi", "5Cs!"] ] result LC OK – got it. But what about that name?
List Comprehensions? >>> [ 2*x for x in [0,1,2,3,4,5] ] [0, 2, 4, 6, 8, 10] Is this really the best name Guido Van Rossum could think of?
List Comprehensions? >>> [ 2*x for x in [0,1,2,3,4,5] ] [0, 2, 4, 6, 8, 10] FunLists! Datafuncs? Google map s? A list comprehension by any other name would be as sweet…
[ n**2 for n in range(0,5) ] Quiz! [ a*(a-1) for a in range(8) if a%2==1 ] [ -7*b for b in range(-6,6) if abs(b)>4 ] [ s[1::2] for s in ['aces','451!'] ] [ 42 for z in [0,1,2] ] A range of list comprehensions... Write Python's result for each L.C.: [ z for z in [0,1,2] ] I wonder if these will be useful ?! Name(s): ___________________________
Syntax ?! a (frustrated!) rendering of an unfamiliar math problem >>> [ 2*x for x in [0,1,2,3,4,5] ] [0, 2, 4, 6, 8, 10] at first… a jumble of characters and random other stuff
Syntax ?! a (frustrated!) rendering of an unfamiliar math problem >>> [ 2*x for x in [0,1,2,3,4,5] ] [0, 2, 4, 6, 8, 10] at first… a jumble of characters and random other stuff
Syntax ~ is CS's key resource! a (frustrated!) rendering of an unfamiliar math problem which was likely similar to these… Where'd the change happen?
Syntax vs. Semantics Plus – you might consider coming to the Career Fair this Friday at HMC's LAC…
Another Monte Carlo Monty… ? inspiring the “Monty Hall paradox”
Let's make a deal… '63-'86 inspiring the Monty Hall paradox Monty
Let's make a deal: XKCD's take… … but what if you considered the goat the grand prize!? inspiring the Monty Hall paradox Monty
Monte Carlo Monty Hall Suppose you always switch to the other door... What are the chances that you will win the prize ? Run it (randomly) 300 times and see!
Monte Carlo Monty Hall def MCMH( init, sors, N ): """ plays the "Let's make a deal" game N times returns the number of times you win the *Spam!* """ if N == 0: return 0 # don't play, can't win przDoor = choice([1,2,3]) # where the spam (prize) is… if init == przDoor and sors == 'stay': result = 'Spam!' elif init == przDoor and sors == 'switch': result = 'pmfp.' elif init != przDoor and sors == 'switch': result = 'Spam!' else: result = 'pmfp.' print 'You get the', result if result == 'Spam!': return 1 + MCMH( init, sors, N-1 ) else: return 0 + MCMH( init, sors, N-1 ) Your initial choice! 'switch' or 'stay' number of times to play
CS for Insight! a.k.a. How often do we win if we SWITCH vs. STAY ? I hope the prize isn't in Euros!
Monte Carlo Monty Hall! # this runs the game once (staying…) MCMHonce1( 3, 'stay' ) # Run _that_ 3000 times PRIZES = [ MCMH1(3,'stay') for x in range(3000) ] # Look at the first 10 of these print PRIZES[0:10] # Let's find the total number of wins: how to do this… ?!
A B C D E F G H
A B C D E F G H
An example closer to home An overworked CGU student (S) leaves Harg. after their "late-night" breakfast. Each moment, they randomly stumble toward class (W) or their Apartment (E) ACB Apt (S) (W) Harg. Write a program to model and analyze! this scenario... Once the student arrives at the dorm or classroom, the trip is complete. The program should then print the total number of steps taken. hw2pr2 rwpos(s,nsteps)rwsteps(s,low,hi) take nsteps random steps starting at s take random steps starting at s until you reach either low or hi S
An example closer to home An overworked CGU student (S) leaves Harg. after their "late-night" breakfast. Each moment, they randomly stumble toward class (W) or their Apartment (E) ACB Apt (S) (W) Harg. Write a program to model and analyze! this scenario... Once the student arrives at the dorm or classroom, the trip is complete. The program should then print the total number of steps taken. hw2pr2 rwpos(s,nsteps)rwsteps(s,low,hi) take nsteps random steps starting at s take random steps starting at s until you reach either low or hi S How could we create this as an "ASCII" animation?
Nature prefers recursion, too! Recursion's challenge? You need to see BOTH the self-similar pieces AND the whole thing simultaneously!
Yes... and no.Are these rules for real?
Dragon's-blood Tree
There still has to be a base case…
or else!
Cyriak: conceptually disruptive recursion… is the branching, not the single-path variety. handfingers
Python's Etch-a-Sketch the turtle's canvas import time from turtle import * def draw(): shape('turtle') # pause time.sleep(2) # drawing… width(5) left(90) forward(50) right(90) backward(50) down() or up() color('darkgreen') tracer(1) or tracer(0) width(5) degrees! sets if the pen draws or not sets if the pen animates or not (0,0) window_height() window_width() (42,42) pixels!
Single-path recursion def tri(): """ a triangle! """ forward(100) left(120) forward(100) left(120) forward(100) left(120) Let's tri this with recursion: (1) How about any regular N-gon? (2) I don't know about tri, but there sure is NO return … ! def tri( n ): """ draws a triangle """ if n==0: return else: forward(100) # one side left(120) # turn 360/3 tri( n-1 ) # draw rest def poly(n, N): """ draws a triangle """ if n==0: return else: forward(100) # one side left(360.0/N) # turn 360/N poly(n-1, N) # draw rest
def chai(size): """ mystery! """ forward(size) left(90) forward(size/2.0) right(90) forward(size) left(90) forward(size/2.0) right(90) backward(size) What does chai(100) draw? (1) Be the turtle ! Finish rwalk so it draws a "stock-market" path: N steps of 10 pixels each. Use recursion. (2) from random import * def rwalk(N): """ make N 10-pixel steps, NE or SE """ if N == 0: return elif choice(['left','right']) == 'left': else: # this handles 'right' Extra! How could you make it a bull (or a bear) market? one possible result of rwalk(20) left(45) forward(10) What if you called chai(size/2) betw. the right(90) & left(90) calls? ? ? Extra!
def chai(size): """ mystery! """ if size<9: return forward(size) left(90) forward(size/2.0) right(90) forward(size) left(90) forward(size/2.0) right(90) backward(size) How could you add more to each T-tip?Why are there two identical commands in a row ~ twice!? (1) What does chai(100) do?
from random import * def rwalk(N): """ make N 10-px steps, NE or SE """ if N == 0: return elif choice(['left','right'])=='left': left(45) forward(10) right(45) rwalk( N-1 ) else: # 'right' right(45) forward(10) left(45) rwalk( N-1 ) What if we didn't turn back to face east each time? (2) rwalk is a random stock market walk... Extra: Creating a bull (or a bear) market?
hw2pr spiral( initLength, angle, multiplier ) spiral(100,90,0.9) 90 fractal art!
svtree( trunkLength, levels ) svtree( 100, 5 ) levels == 5 levels == 2 levels == 0 (no drawing) levels == 1 levels == 3 levels == 4
svtree( trunkLength, levels ) svtree( 100, 5 ) levels == 5 levels == 2 levels == 0 (no drawing) levels == 1 svtree( 75, 4 ) What steps does the turtle need to take before recursing? levels == 3 levels == 4
svtree( trunkLength, levels ) levels == 5 levels == 4 levels == 3 levels == 2 levels == 0 (no drawing) Be sure the turtle always returns to its starting position! levels == 1 svtree( 100, 5 ) step #1: go forward… step #2: turn a bit… step #3: draw a smaller svtree! step #4: turn to another heading step #5: draw another smaller svtree! step #6: get back to the start by turning and moving!
svtree( trunkLength, levels ) svtree( 100, 5 ) levels == 5 levels == 2 levels == 0 (no drawing) levels == 1 svtree( 75, 4 ) Be sure the turtle always returns to its starting position! that means it will finish the recursive call right here! levels == 3 levels == 4
The Koch curve snowflake(100, 0)snowflake(100, 1)snowflake(100, 2) snowflake(100, 3)snowflake(100, 4)snowflake(100, 5)
Recursive art? Create your own… hw2pr4 Happy turtling! What? Way too happy to be art… My recursive compositions burninate even Cyriak's brain! seven-cornered confetti
A B C D E F G H
Data Functions [13,14,15] sum( ) [3,4,5,6,7,8,9]
sum range def sum(L): """ input: L, a list of #s output: L's sum """ if len(L) == 0: return 0.0 else: return L[0] + sum(L[1:]) Base Case Recursive Case
def range(low,hi ): """ input: ints low and hi output: int list from low to hi """ if low >= hi: return [] else: return what's cookin' here? excluding hi sum range step?
>>> sum(range(1,101)) ? sum and range I'd bet you have a 50/50 chance on this one… - Ben. L '
>>> sum(range(1,101)) ? sum and range I'd bet you have a 50/50 chance on this one… - Ben. L ' and 100 more… Gauss's storied story…
sum and range Looks sort of scruffy for a 7-year old… ! and 100 more… >>> sum(range(1,101)) 5050 Gauss's storied story…
Welcome to CS 5! Be sure to watch your head…
Designing with LCs >>> [ _______ for x in range(4) ] [0, 14, 28, 42] >>> [ _____________ for c in 'igetthis' ] [True, False, False, False, False, False, True, False] [ 1, 0, 0, 0, 0, 0, 1, 0 ] output input And what if we wanted these values in red…? [0,1,2,3]
LC = [1 for x in L] return sum( LC ) myst1(L): def [7,8,9] But one-liners are my specialty… Sweet! Using LCs LC = [letScore(c) for c in S] return sum( LC ) myst2(S): def 'twelve' lots of ifs… return score letScore(c): def
LC = [1 for x in L] return sum( LC ) len(L): def [7,8,9] But one-liners are my specialty… Sweet! Using LCs LC = [letScore(c) for c in S] return sum( LC ) scrabbleScore(S): def 'twelve' lots of ifs… return score letScore(c): def
stuff to get score… return score letScore(c): scrScore(s): LC = [ letScore(c) for c in s] return sum( LC ) def scrabble score! 'twelve' Using LCs
LC = [1 for x in L] return sum( LC ) len(L): def But in one-line … ? 'cs5' len(L): def return sum([1 for x in L]) Maybe too terse! Sweet! "One-line" LCs
count(e,L): LC = [ 1 for x in L if ] return sum( LC ) def sajak(s): LC = [ for c in s] return sum( LC ) # of vowels Remember True == 1 and False == 0 def 'sequoia' count(e,L): # of times e is in L LC = [ for x in L ] return sum( LC ) def [3,42,5,7,42] 42 using if instead…
Go! Write each of these functions using list comprehensions… def nodds(L): def lotto(Y,W): input: L, any list of #s output: the # of odd #s in L example: nodds( [3,4,5,7,42] ) == 3 inputs: Y and W, two lists of "lottery" numbers (ints) output: the # of matches between Y & W example: lotto( [5,7,42,47], [3,5,7,44,47] ) == 3 Y are your #sW are the winning #s def ndivs(N): input: N, an int >= 2 output: the # of positive divisors of N example: numdivs(12) == 6 (1,2,3,4,6,12) LC = [ for x in L ] return sum(LC) def primesUpTo(P): input: P, an int >= 2 output: the list of prime #s up to + incl. P example: primesUpTo(12) == [2,3,5,7,11] Extra! LC = [
output: the # of odd #s in L def nodds(L): inputs: L, any list of #s example: nodds( [3,4,5,7,42] ) == 3 LC = [ for x in L ] return sum(LC) def nodds(L): LC = [ 1 for x in L ] return sum(LC)
def lotto(Y,W): LC = [ for w in W ] return sum(LC) def lotto(Y,W): LC = [ 1 for w in W ] return sum(LC) inputs: Y and W, two lists of "lottery" numbers (ints) output: the # of matches between Y & W example: lotto( [5,7,42,47], [3,5,7,44,47] ) == 3 Y are your #sW are the winning #s … could adapt to "y in Y", too.
def ndivs(N): LC = [ for n in range(1,N+1)] return sum(LC) input: N, an int >= 2 output: the # of positive divisors of N example: numdivs(12) == 6 (1,2,3,4,6,12) def primesUpto( P ): Extra! A list of all primes up to P…
Turtle happiness? remember you can run done() after turtle drawing! This releases control of the turtle window to the computer (the operating system). To be honest, it seems that not every machine needs this…
We're computationally complete! CS 5: now recursing… putting Python to work! Hw 2 – due Sunday evening ~ usual time pr1 lab – Turtle! pr2, pr3 - Python probs… What's next? pr0 reading – Watson! Or re-cursing, depending on your feelings about recursion! pr4 – extra-credit turtle… ! & adding building-blocks
Recursive Art ~ hw2pr4 Submit things that work … … and even things that don't! septagonal confetti Cyriak's pet snake…
On-campus tutoring hours… ! CMC: Scripps: Pitzer! Pomona: Beckett Lounge, Mon 6-8 (Melody S) CSMath CR, Adams Hall, Sun 8-10 (Rachel L & Justin A) Edmunds, Sun 8-10 (Kim R) Scripps Student Union, Sun 6-8 (Kate F) Phillips Lounge, Sun 3-5 (Michael I) Beckett Lounge, Sat + Sun 1-3 (Melody S) Motley, Thu 8-10 (Kate F)
Picture it! Try it! def power(b,p): Handle negative values of p in an elif. """ returns b to the p power Use recursion, not ** Inputs: int b, int p: the base and the power """ Want more power? power(2,5) -> 2*2 For example, power(5,-1) == 0.2 if : Base case test p == 0 return else: return Base case Recursive case power(2,0) -> == *2* p 2*2* 2 4 power(2,p) -> 2*power(…) Do you see the call to power!?
Using a Euro, Yuan the deadline comes next time I'll try to make the Mark. I'm Pounding it into my brain, but I have to Peso much attention to other deadlines in the classes I Currency have - but if this keeps up, my grade will be demolished into Rubles. hw0 highlights and thoughts… Eric Nguyen Ashraf Mathkour Martha Parker ! 11:59 !?
Using a Euro, Yuan the deadline comes next time I'll try to make the Mark. I'm Pounding it into my brain, but I have to Peso much attention to other deadlines in the classes I Currency have - but if this keeps up, my grade will be demolished into Rubles. hw0 highlights and thoughts… Eric Nguyen Ashraf Mathkour Martha Parker ! 11:59 !? on-campus tutoring! (details Wed. …)
Run inside Sublime!? not required + not universal -- but you may want to try it… Python interpreter Python source instructions are at the start of this week's Lab…
Try it! def power(b,p): Handle negative values of p in an elif. """ returns b to the p power Use recursion, not ** Inputs: int b, int p: the base and the power """ Want more power? power(2,5) == 32.0 For example, power(5,-1) == 0.2 def sajak(s): sajak('wheel of fortune') == 6 What 7-letter English word w maximizes sajak(w) ? What about y? You decide… """ returns the number of vowels in the input string, s """ Want more Pat? if : if s == '' : Base case test elif else: p == 0 return else: return Base case test elif in general:
Picture it! def mylen(s): """ returns the number of characters in s input: s, a string """ mylen('') 0 NOT a space – this is no characters at all. This is the empty string – and it has length of 0! s = '' mylen('hi') 1+mylen( ) mylen('recursion') 1+mylen( ) s = 'recursion' starts with a vowel – count that vowel and delegate the rest to recursion wow! s = 'hi' if : Base case test p == 0 return else: return Base case Recursive case
Design #2 def mymax(L): """ input: a NONEMPTY list, L output: L's maximum element """ if : elif : base case test! another case… len(L)==1 [1,4,3,42,-100,7] return else:
def mymax(L): """ input: a NONEMPTY list, L output: L's maximum element """ if len(L) == 1: return L[0] elif L[0] < L[1]: return mymax( L[1:] ) else: return mymax( L[0:1] + L[2:] ) Hey - do I get a slice?! Design #2
Computation's Dual Identity name: x type: int LOC: memory location 300 ComputationData Storage name: y type: int LOC: memory location 304 Last time variables ~ boxes But what does all this stuff look like ?
Giving names to data def flipside(s): """ flipside(s): swaps s's sides! input s: a string """ x = len(s)/2 return s[x:] + s[:x] This idea is the key to your happiness!
Hey - I'm happy about this, too! Why would computers "prefer" the top version, too? def flipside(s): x = len(s)/2 return s[x:] + s[:x] def flipside(s): return s[len(s)/2:] + s[:len(s)/2] Use variables! Avoid this approach…
control-b in Sublime Test! def flipside(s): """ flipside(s): swaps s's sides! input s: a string """ x = len(s)/2 return s[x:] + s[:x] # # Tests! # print "flipside('carpets') ~", flipside('carpets') (1) function definition (2) function tests We provide these tests (for now…) but don't test like this!
def convertFromSeconds(s): # total seconds """ convertFromSeconds(s): Converts an integer # of seconds into a list of [days, hours, minutes, seconds] input s: an int """ seconds = s % 60 # leftover seconds m = s / 60 # total minutes minutes = m % 60 # leftover minutes h = m / 60 # total hours hours = h % 24 # leftover hours days = h / 24 # total days return [days, hours, minutes, seconds] This program uses variables constantly! Using variables…
def convertFromSeconds(s): # total seconds """ convertFromSectons(s): Converts an integer # of seconds into a list of [days, hours, minutes, seconds] input s: an int """ seconds = s % 60 # leftover seconds m = (s / 60) # total minutes minutes = m % 60 # leftover minutes h = m / 60 # total hours hours = h % 24 # leftover hours days = h / 24 # total days return [days, hours, minutes, seconds] How functions look… docstring comments – these are mostly optional in CS 5 code block return statement name input(s)
Computation's Dual Identity name: x type: int LOC: memory location 300 ComputationData Storage name: y type: int LOC: memory location 304 variables ~ boxes accessed through functions… Functions! It's no coincidence this starts with fun!
Functioning across disciplines def g(x): return x**100 g(x) = x 100 CS's googolizerMath's googolizer defined by what it does defined by what it is + how efficiently it works procedurestructure
How functions work… def f(x): return 11*g(x) + g(x/2) What is demo(-4) ? def demo(x): return x + f(x) def g(x): return -1 * x I might have a guess… Quiz First and last name(s): -4
How functions work… def f(x): return 11*g(x) + g(x/2) >>> demo(-4) ? def demo(x): return x + f(x) demo x = -4 return -4 + f(-4) def g(x): return -1 * x Hey! This must be a stack-frame frame! stack frame
def f(x): return 11*g(x) + g(x/2) def demo(x): return x + f(x) demo x = -4 return -4 + f(-4) f x = -4 return 11*g(x) + g(x/2) def g(x): return -1 * x >>> demo(-4) ? How functions work… stack frame
def f(x): return 11*g(x) + g(x/2) def demo(x): return x + f(x) demo x = -4 return -4 + f(-4) f x = -4 return 11*g(x) + g(x/2) def g(x): return -1 * x >>> demo(-4) ? How functions work… These are distinct memory locations both holding x 's. stack frame
def f(x): return 11*g(x) + g(x/2) def demo(x): return x + f(x) demo x = -4 return -4 + f(-4) f g x = -4 return 11*g(-4) + g(-4/2) x = -4 return -1.0 * x def g(x): return -1 * x >>> demo(-4) ? How functions work… stack frame
def f(x): return 11*g(x) + g(x/2) def g(x): return -1 * x def demo(x): return x + f(x) demo x = -4 return -4 + f(-4) f g x = -4 return 11* 4 + g(-4/2) x = -4 return -1 * -4 4 >>> demo(-4) ? How functions work… done! stack frame
def f(x): return 11*g(x) + g(x/2) def g(x): return -1 * x def demo(x): return x + f(x) demo x = -4 return -4 + f(-4) f x = -4 return 11* 4 + g(-4/2) >>> demo(-4) ? How functions work… the "return value" stack frame
def f(x): return 11*g(x) + g(x/2) def g(x): return -1 * x def demo(x): return x + f(x) demo x = -4 return -4 + f(-4) f g x = -4 return 11* 4 + g(-4/2) x = -2 return -1 * -2 2 >>> demo(-4) ? How functions work… These are distinct memory locations both holding x 's – and now they also have different values!! stack frame
def f(x): return 11*g(x) + g(x/2) def g(x): return -1 * x def demo(x): return x + f(x) demo x = -4 return -4 + f(-4) f x = -4 return 11* >>> demo(-4) ? How functions work… the "return value" stack frame
def f(x): return 11*g(x) + g(x/2) def g(x): return -1 * x def demo(x): return x + f(x) demo x = -4 return -4 + f(-4) f x = -4 return 11* >>> demo(-4) ? How functions work… stack frame
def f(x): return 11*g(x) + g(x/2) def g(x): return -1 * x def demo(x): return x + f(x) demo x = -4 return >>> demo(-4) 42 How functions work… stack frame
Function stacking def f(x): return 11*g(x) + g(x/2) def g(x): return -1 * x def demo(x): return x + f(x) demo x = -4 return -4 + f(-4) f g x = -4 return 11* 4 + g(-4/2) x = -2 return -1 * -2 2 (1) keeps separate variables for each function call… (2) remembers where to send results back to… "The stack" is a memory area that the stack stack frame
return > print >>> ans = dbl(21) def dbl(x): """ dbls x? """ return 2*x def dblPR(x): """ dbls x? """ print 2*x >>> ans = dblPR(21)
>>> ans = dbl(21) def dbl(x): """ dbls x? """ return 2*x def dblPR(x): """ dbls x? """ print 2*x >>> ans = dblPR(21) return yields the function call's value … print just prints stuff to the screen... … which the shell then prints! return > print
Function design
Thinking sequentially 5 ! = 5 * 4 * 3 * 2 * 1 N ! = N * (N-1) * (N-2) * … * 3 * 2 * 1 factorial 5 ! = 120
factorial 5 ! = 5 * 4 * 3 * 2 * 1 N ! = N * (N-1) * (N-2) * … * 3 * 2 * 1 5 ! = 120 March + beyond… Thinking sequentially
Recursion == self-reference! 5 ! = N ! = Thinking recursively 5 ! = 5 * 4 * 3 * 2 * 1 N ! = N * (N-1) * (N-2) * … * 3 * 2 * 1 factorial 5 ! = 120
Warning: this is legal! def fac(N): return N * fac(N-1) I wonder how this code will STACK up!?
def fac(N): return N * fac(N-1) The calls to fac will never stop: there's no BASE CASE! Make sure you have a base case, then worry about the recursion... legal != recommended
def fac(N): return fac(N) Roadsigns and recursion examples of self-fulfilling danger This runs ~ but does not help!
Recursion's advantage: It handles arbitrary structural depth – all at once! As a hat, I'm recursive, too!
The dizzying dangers of having no base case!
def fac(N): if N <= 1: return 1 Thinking recursively Base case
def fac(N): if N <= 1: return 1 Thinking recursively Base case "How could I use the factorial of anything smaller than N?" Then do! Ask yourself:
def fac(N): if N <= 1: return 1 else: return N*fac(N-1) Recursive case (shorter) Human: Base case and 1 stepComputer: Everything else Base case Thinking recursively
def fac(N): if N <= 1: return 1 else: rest = fac(N-1) return rest * N Recursive case (clearer, for some) Human: Base case and 1 stepComputer: Everything else Base case Thinking recursively
Behind the curtain… fac(5) def fac(N): if N <= 1: return 1 else: return N * fac(N-1)
fac(5) 5 * fac(4) Behind the curtain… def fac(N): if N <= 1: return 1 else: return N * fac(N-1)
fac(5) 5 * fac(4) 4 * fac(3) Behind the curtain… def fac(N): if N <= 1: return 1 else: return N * fac(N-1) 5 * Operation waiting …
fac(5) 5 * fac(4) 4 * fac(3) 3 * fac(2) Behind the curtain… def fac(N): if N <= 1: return 1 else: return N * fac(N-1) 5 * 5 * 4 * More operations waiting…
fac(5) 5 * fac(4) 4 * fac(3) 3 * fac(2) 2 * fac(1) Behind the curtain… def fac(N): if N <= 1: return 1 else: return N * fac(N-1) 5 * 5 * 4 * 5 * 4 * 3 *
fac(5) 5 * fac(4) 4 * fac(3) 3 * fac(2) 2 * fac(1) 1 "The Stack" Stack frames hold all of the individual calls to fac Behind the curtain… def fac(N): if N <= 1: return 1 else: return N * fac(N-1) N=5 N=4 N=3 N=2 N=1 5 different N's are living in memory… 5 * 5 * 4 * 5 * 4 * 3 * 5 * 4 * 3 * 2 *
fac(5) 5 * fac(4) 4 * fac(3) 3 * fac(2) 2 * 1 Behind the curtain… def fac(N): if N <= 1: return 1 else: return N * fac(N-1) 5 * 5 * 4 * 5 * 4 * 3 *
fac(5) 5 * fac(4) 4 * fac(3) 3 * 2 Behind the curtain… def fac(N): if N <= 1: return 1 else: return N * fac(N-1) 5 * 5 * 4 *
fac(5) 5 * fac(4) 4 * 6 Behind the curtain… def fac(N): if N <= 1: return 1 else: return N * fac(N-1) 5 *
fac(5) 5 * 24 Behind the curtain… def fac(N): if N <= 1: return 1 else: return N * fac(N-1)
fac(5) Result: x*** -> N 0 0 N*** -> X 1 Look familiar? Recursive step Base case show facWPR … Behind the curtain… def fac(N): if N <= 1: return 1 else: return N * fac(N-1)
Thinking recursively… What will print when facWPR(5) is called?
def fac(N): if N <= 1: return 1 else: rest = fac(N-1) return rest * N You handle the base case – the easiest case! Recursion does almost all of the rest of the problem! You specify one step at the end Let recursion do the work for you. Exploit self-similarity Produce short, elegant code Less work !
But you do need to do one step yourself… def fac(N): if N <= 1: return 1 else: return fac(N) This will not work !
Nature prefers recursion, too! Recursion's challenge? You need to see BOTH the self-similar pieces AND the whole thing simultaneously!
Yes... and no.Are these rules for real?
Dragon's-blood Tree
There still has to be a base case…
or else!
Recursive design… (2) Find the self-similarity. (1) Program the base case. (3) Do one step! (4) Delegate the rest to recursion… fun! easy!? cool! Aha! indexing and slicing
One step? …is easy to do with Python s = 'aliens' How do we get at the initial character of s? L = [ 42, 21 ] How do we get at ALL THE REST of s? s[0] L[0] s[ ] L[ ] How do we get at the initial element of L? How do we get at ALL THE REST of L? 'liens' [ 21 ]
Recursive design #1 def mylen(s): """ input: any string, s output: the number of characters in s """ if : else: base case test! 'cs5' "How could I make use of the length of something smaller than s?" Then do! Ask yourself: return
… complete def mylen(s): """ input: any string, s output: the number of characters in s """ if s == '': return 0 else: return 1 + mylen(s[1:]) There's not much len left here!
mylen('cs5') Behind the curtain: how recursion works... def mylen(s): if s == '': return 0 else: return 1 + mylen(s[1:]) 1 + mylen('s5') mylen('5') mylen('')
Looking behind the curtain…
Design #2 def mymax(L): """ input: a NONEMPTY list, L output: L's maximum element """ if : elif : base case test! another case… len(L)==1 [1,4,3,42,-100,7] return else:
def mymax(L): """ input: a NONEMPTY list, L output: L's maximum element """ if len(L) == 1: return L[0] elif L[0] < L[1]: return mymax( L[1:] ) else: return mymax( L[0:1] + L[2:] ) Hey - do I get a slice?! Design #2
mymax( [1,4,3,42,-100,7] ) def mymax(L): if len(L) == 1: return L[0] elif L[0] < L[1]: return mymax( L[1:] ) else: return mymax( L[0:1]+L[2:] ) mymax( [4,3,42,-100,7] ) mymax( [4,42,-100,7] ) mymax( [42,-100,7] ) mymax( [42,7] ) mymax( [42] ) 42 base case drop 1st drop 2nd
Try it! def power(b,p): Handle negative values of p in an elif. """ returns b to the p power Use recursion, not ** Inputs: int b, int p: the base and the power """ Want more power? power(2,5) == 32.0 For example, power(5,-1) == 0.2 def sajak(s): sajak('wheel of fortune') == 6 What 7-letter English word w maximizes sajak(w) ? What about y? You decide… """ returns the number of vowels in the input string, s """ Want more Pat? if : if s == '': Base case test elif else: p == 0 return else: return Base case test elif in general:
power( 2, 5 ) 2* power( 2, 4 ) 2* 2* power( 2, 3 ) 2* 2* 2* power( 2, 2 ) 2* 2* 2* 2* power( 2, 1 ) 2* 2* 2* 2* 2* power( 2, 0 ) 2* 2* 2* 2* 2* 1 32
sajak( 'eerier' ) 1+ sajak( 'erier' ) sajak( 'rier' ) sajak( 'ier' ) sajak( 'er' ) sajak( 'r' ) sajak( '' )
def power(b,p): """ inputs: base b and power p (an int) implements: b**p """ if p == 0: return 1.0 elif p < 0: return ____________________ else: return b * power(b,p-1) Recursion is power!
# of vowels in s sajak(s): Base case? When there are no letters, there are ZERO vowels if s[0] is NOT a vowel, the answer is Rec. step? Look at the initial character. if s[0] is a vowel, the answer is DESIGNDESIGN sajak( s[1:] ) 1 + sajak( s[1:] )
def sajak(s): if s == '': return 0 elif s[0]=='a' or s[0]=='e' or… but how to check for vowels? Is s[0] a vowel?
Python is… in >>> 'i' in 'team' False >>> 'cs' in 'physics' True >>> 42 in [41,42,43] True >>> 42 in [[42], '42'] False I guess Python's the in thing >>> 'i' in 'alien' True >>> 3*'i' in 'alien' False
def sajak(s): if len(s) == 0: return 0 elif s[0] in 'aeiou': return 1 + sajak(s[1:]) else: return 0 + sajak(s[1:]) if s[0] is NOT a vowel, the answer is just the number of vowels in the rest of s if s[0] IS a vowel, the answer is 1 + the # of vowels in the rest of s Base Case Recursive Cases let's input 'eerier' for s
The key to understanding recursion is, first, to understand recursion. - a former CS 5 student LAC W/Th/F/Sa/Su Good luck with Homework #1 It's the eeriest!
Nature prefers recursion, too! Recursion's challenge? You need to see BOTH the self-similar pieces AND the whole thing simultaneously!
Yes... and no.Are these rules for real?
Dragon's-blood Tree
There still has to be a base case…
or else!