Review: Subroutines in Python Subroutines (a.k.a. routines and procedures) are a way to break a program up into named little pieces. If we do it right, these pieces work like building blocks that can be reused. Using subroutines, we can solve a problem once and then use that solution wherever we need it. We have already been using subroutines that are provided by Python: raw_input(), randint(), range(), etc. But as you know, we can also make our own. Here's a very simple subroutine definition: def sayGoodbye(): print "Goodbye, my friend, goodbye. . . " return Then here's where we use it (If we want to be fancy, we say we call or invoke it): . . . if userResp == "no": sayGoodbye() . . . Subroutine Parameters You provide information to a subroutine by passing values to it. These values are called parameters, or arguments. We’ve been doing this for a while: numToGuess = random.randint(3,20) userResp = raw_input(“Play again?”) When you define a subroutine, you have to provide a list of any parameters that it requires. In the subroutine say_goodbye() below, the parameter is mood: def say_goodbye(mood): if mood == "happy": print "cheerio old chum!" elif mood == "grumpy": print "get lost, you bum" else: print "I don't know how to be", mood return # what will these do? say_goodbye("grumpy") say_goodbye("happy") say_goodbye("perplexed") One way to think about it. . . • Think of the parameters as food for the subroutine. You can think of the parentheses as the subroutine's mouth: def feed_me(food): print "me like", food return feed_me("krill") feed_me("hamburgers") More on Parameters Subroutines can have multiple parameters: def find_volume(length, width, height): vol = length * width * height print "the volume is", vol return When you define subroutine parameters, you're saying "Here is the information that this subroutine needs to do its job", and you're giving each piece of information a name so that the subroutine can use it. We say that the values for the parameters are passed to the subroutine. The parameters are identified by their position (or order) inside the parentheses when the subroutine is called. For example, when this call is made: find_volume(ln, 35, 2*num) then inside the subroutine, length will have the value of the variable ln, width will have the value 35, and height will have a value 2 times the value of the variable num. Make Sense? def test(first, second): # what will this print? print first, second return first = "yo" second = "pardner" test(first, second) # what will the line below print? print first, second What will be printed when we call main()? Wait, there's more. . . • Within a subroutine, the parameters act like variables that get their initial values from whatever was passed in. • But like a bubble popping, those variables disappear when the subroutine returns. Ponder this: def test(first, second): # what will this print? print first, second first = "greetings" second = "howdy" return first = "yo" second = "pardner" test(first, second) # what will the line below print? print first, second) Two kinds of subroutines A subroutine has a job – that is, it does something for us. There are two kinds of subroutines: 1) Some subroutines just do their job, and don’t give us back a value. Examples: turn_left() circle(50, 50, radius) 2) Some subroutines do something, and give us back a value that we can use later on in our program. We say that they return a value. Subroutines that return values are frequently called functions. Examples: numsBy3List = range(1,100,3) userResp = raw_input(“Play again?”) numToGuess = random.randint(1,20) print "There are", len(myList), "items" Continuing the analogy. . . If subroutines can eat, it stands to reason that they can also, shall we say, poop*. That is, they can give you something back after they've done their job. More specifically, they can return a value. def fahrenheitToC(fahrenheitTemp ): retval = (fahrenheitTemp – 32) * 5 / 9.0 Means "return this value back to whoever called us" return retval >>> temp = fahrenheitToC(212) Whatever fahrenheitToC() returned will become the new value of temp (We've already seen this kind of thing with raw_input(), which poops a string that the user typed, and range(), which poops a list of integers.) *Thanks to Kirby Urner for this illuminating metaphor! # this subroutine makes an <h1> heading for # the parameter text def makeHtmlHeading(text): return "<h1>" + text + "</h1>" # this subroutine returns the larger of the # two parameters def max(val1, val2): if val1 > val2: return val1 else: return val2 Some return Examples Using these subroutines: >>> print makeHtmlHeading("Python Hates Me") <h1>Python Hates Me</h1> >>> print max(123,32) 123 # this subroutine returns the absolute # value of the parameter def abs(val): if val > 0: return val else: return –val # subroutine returns a Boolean value: # True if an odd number, otherwise False def isOdd(val): if val % 2 == 1: return True else: return return False Using these subroutines: >>> print abs(-17) 17 >>> print isOdd(17) True More Examples Returning Boolean Values Subroutines can return Boolean values – True or False: def startsWithVowel(word): if word[0] in "aeiouAEIOU": return True else: return False If the subroutine returns True or False, it can be used in a condition for an if or while statement: userWord = raw_input("Gimme a word:") if startsWithVowel(userWord): print userWord + "starts with a vowel") else: print userWord + "does not start with a vowel" We saw this kind of thing in RUR-PLE: front_is_clear(), on_beeper() and the others are just subroutines that return True or False Why return when you can print? • There is a big difference between a subroutine printing a result and returning a result • when a subroutine prints a result, it is displayed to the human user, but that does not give the code that called the subroutine access to the value – the subroutine is a "black box" to the rest of the program • when a subroutine returns a result, that value is available to whatever called it – to be saved in a variable, used for further calculation, and so forth Some Harder Examples # this subroutine returns the largest value in a # list of numbers def maxInList(numList): maxVal= numList[0] # initial value has to be in the list for nxt in numList: if nxt > maxVal: maxVal = nxt return maxVal # this subroutine returns a count of the number of times # that the letter letter appears in the string word. def count(word, letter): numTimes = 0 for nxt in word: if nxt == letter: numTimes += 1 return numTimes Using these subroutines: >>> print max([1,44,932,34]) 932 >>> print count("aardvark", "a") 3 Something Useful: readInt() def readInt(prompt): while True: try: ret = int(raw_input(prompt)) break except: print "Please enter an integer:" return ret Then your extra-credit #2 guessing game program could be written: low = readInt("Enter your low number:") high = readInt("Enter your high number:") and so forth. . . See how parameters and return values make this work? Something Useful: a Quadratic Formula Solver ax2 + bx + c = 0 import math # pass in the values for the coefficients a, b, and c # subroutine returns a list of the real roots, # or an empty list if it has imaginary roots def quadratic(a, b, c): discriminant = (b ** 2) - (4 * a * c) if discriminant < 0: return [] # not ready for imaginary numbers! else: r1 = (-b + math.sqrt(discriminant))/(2.0 * a) r2 = (-b - math.sqrt(discriminant))/(2.0 * a) return [r1, r2] Call stack • Question: When a subroutine returns, where does it return to? The subroutine doesn't know or care – but the Python runtime has to know where to go next. • The address of the next line of code in the caller is pushed onto the call stack before the subroutine is called. • If that subroutine then calls another subroutine, that second return address is pushed on top of the previous one. • And so forth. . . • When a subroutine returns, it returns to the address on top of the stack. That address is popped off the stack, exposing the one beneath it. Recursion # returns factorial of num def factorial(num): if num <= 1: return 1 else: return num * factorial(num – 1) # what will this do? def blowup(): return blowup() File "<pyshell#2>", line 2, in blowup blowup() RuntimeError: maximum recursion depth exceeded