################# Notes on Functions in Python ##################### ######################################################################## ## Functions (also called procedures or "methods") form the most important # component in a well designed program. When we design and implement an # algorithm we want to be able to use it multiple times, on different # input values. Furthermore, we want our implementation code to be # self-contained so that we can easily integrate it into larger programs. ## Let's start with a relatively simple example. #### function to compute the circumference of a circle given its radius: def circumference(r): return 3.1415927 * r * 2 # end of function # As with if-else statements and while loops, the body of the function # is indicated by correct indentation and alignment. # Here, "circumference" is the name of the function and "r" is an # *argument* or *parameter*, which represents the input to the function. # By "input" here I do not mean user input - just the value that the function # requires before it can compute a result. ## To use the function, we *call* it: print circumference(4) # will print the circumference of a circle with a radius value of 4. # Note that circumference(4) here is used in place of a numerical expression # or VALUE (as opposed to a statement). This is a consequence of the # *return* statement inside a function. THE VALUE OF THE FUNCTION CALL # IS THE VALUE IT RETURNS. Unless there is a return statement before the # function terminates, it will not return a value. For example: def f(n): n = n+1 # print f(3) ## ERROR! because the function did not *return* a value. # When a function does not return a value, we can use it as a statement as # opposed to a value or expression: def repeatprint(n,s): while n>0: print s n = n-1 # end while # end function ## now call the function: repeatprint(10, "hello world") # this will print "hello world" 10 times. # This function call is a statement as opposed to a value or expression. # Usually, however, functions will return values. Note that this function # also take two arguments: the first is a number and the second one a string. ### Some more examples: ## function to return the greatest common divisor of integers a and b: def gcd(a,b): while (a!=0 and b!=0): if a>b: a = a%b else: b=b%a # while return a+b ## This is the value returned by the function. #gcd print gcd(8,12) # prints 4 ## function to return the nth Fibonacci number: def fib(n): a,b = 1,1 # initialize the first two fib numbers while n>1: a,b = b,a+b # compute the next two fib numbers n -= 1 # while return b #fib ############ IMPORTANT THINGS TO WATCH OUT FOR ############# # Here are two common mistakes made by beginners: ## 1. Confuse defining a function with calling a function. # When you define a function using *def*, absolutely nothing happens. # You need to CALL it in order for the code of the function to be executed. ## 2. Confuse where to put I/O (input/output). # Sometimes I see beginners write code like this: def circumference(r): r = input("enter radius") return 3.1415927 * r *2 # or def circumference(r): print 3.1415927 * r * 2 ## The problem with these functions is that they do not SEPARATE IO FROM # COMPUTATION. In general, you should not put input and print statements # inside a function. A function can be used in a variety of I/O settings, # for example, using a graphical user interface, or taking input from a # network connection. The function should focus on the ALGORITHM that # it's supposed to implement, and leave IO to other parts of the program. # Thus we should arrange IO to be outside the function definition: n = input("enter a number:") # input answer = fib(n) # computation print "the", n, "the Fibonacci number is", answer # output ## There are two exceptions to not putting print statements inside functions: # 1. when the only purpose of the function is to print something. For # example, when you want to print a large set of data in a certain format, # and you want to encapsulate the procedure for printing it in the right form. # 2. when you want to TRACE your program for debugging purposes, you can # temporarily insert print statements into your program to see what your # code is doing or not doing at certain points. These print statements # should be commented out or removed after debugging. #### How to comment a function. Large software systems are written by # multiple programs. When you write a function, you need to provide # enough information to a programmer so that he or she knows how to call # the function without having to look at how the function is written. # Specify what each input parameter should represent and what return value # (if any) should be expected. Be especially clear as to the *type* of # the parameters and return value. #### Exercise: write a function to compute n!. For example, 5! is # 5 * 4 *3 * 2 * 1, 3! is 3* 2 * 1. 0! is 1. The function should take # n as an argument and return n! as a value. ######### Using functions with arrays: # For more interesting functions, we can can use loops with arrays. # arrays can be passed as arguments to a function, as well as be # returned by a function as its value. ### Function to add up all the number in an array. def sum(A): # return sum of all numbers inside array A ax = 0 # accumulator i = 0 # loop counter/array index while ianswer: answer = b if c>answer: answer = c return answer ## Can you generalize this algorithm into a loop on an array of numbers? ## answer to exercise: def largest(A): answer = A[0] # accumulator holds the value computed so far i = 1 # array index starts at 1 since 0 already accounted for while i answer: answer = A[i] i += 1 # while return answer ##largest ############ #### When writing the function, you need to first be clear as to the following: # 1. What precisely is the function supposed to compute # 2. How many parameters does the function take # 3. What does each parameter represent # 4. What is the *type* of each parameter (int, string, array, etc...) # 5. Whether the function should return a value # 6. The type and purpose of the return value, if there is one. ############ ################ LOCAL VARIABLES: # Does "x" always refer to the same value? x = 1 def inc(x): # function that changes value of x!! x = x+ 1 print x # this will print 2 #inc print x # This WILL PRINT 1, NOT 2! # Why? Because there are two different variables named x. The x # inside the function definition is isolated from other variables that # might be called x in the rest of your program. It's similar to words like # "it." "It" means different things in different contexts. Inside # a function, all parameter (argument) variables, and all variables THAT ARE # ASSIGNED TO in the body of the function, are local to the function. They # cannot be referred to outside the function. #Read and consider this more complicated example carefully: a = 1 b = 2 c = 3 def f(a): d = a + b c = d + 1 return c #f print f(5) # prints 8 print a # prints 1 print b # prints 2 print c # prints 3 #print d # ERROR. d is not defined here ## In a modern programming language, VARIABLES have scope. You probably # already sensed this: the argument variable A in the sum and indexof functions # above are not the same: their meaning and usage is local to a function. # But the story is a bit more complicated, and Python has a very specific # set of rules governing the scope of visibility of variables. ## The "shocker" is that there are 2 variables named *a* in the program: # one has "global" scope (a = 1), and one exists only inside the function. # the a inside the function is created when the function is called, and # initially assigned the value of the parameter passed in. However, # when the function exits (after it returns), the variable *a* still refers # to the a=1 defined outside the function. ## There are also 2 variables named *c* in the program: one is declared by # c=3, and exists globally, and the other is declared by c = d + 1 # inside the function, and is only used inside the function. When the # function call ends, *c* will again refer to the global c. ## There is a variable named *d*, but it exists only inside function calls # to f. d was never assigned a value outside the function, so it is only # meaningful inside the function call. ## However, there is only one *b* in the program, which refers to b=2. # The *b* outside the function and the *b* inside the function are the same. # This is because the code inside f only READ the value of *b*, it did not # attempt to WRITE (assign) to it. In contrast, it did assign a value # to *c*, and that's why *c* became a local variable but *b* did not. ## SO REMEMBER: ANY VARIABLE YOU ASSIGN TO INSIDE A FUNCTION BECOMES LOCAL ## TO THAT FUNCTION. ## To summarize, the LOCAL VARIABLES of a function consist of all the # parameter variables AND all variables that are assigned to inside the # body of the function. ####### What does the following code do? (study carefully): def swap(x,y): x,y = y,x # tries to swap the two values. # swap x = 1 y = 2 swap(x,y) print x,y # what will this print? # ANSWER: it still prints 1,2. WHY????? #There is a way for a function to change the value of an external variable #by declaring it "global": c = 1 def f(n): global c c = c + n # f(2) print c # c is now 3. ## HOWEVER, the global definition is to be used sparingly. There is a reason # why we want to limit the scope of variables. A function should be a # "plug and play" piece of code: it should be used to compose a variety of # different programs. In general, we do NOT want assignments to variables # inside the function to interfere with other values outside of the function. # We want to function to be self-contained. This also makes changing or # debugging the function much easier. The highest cost in software # development is often not in getting it to work initially, but in being able # to maintain and upgrade it over time. # Most of the variables that we use will be local inside functions (or # inside classes, as we'll learn later). Global variables should be used # very sparingly. Some programming languages such as Java do not even allow # any variables to be global. ## Finally, just as we like to use x, y, z, etc. to name our variables, we # also sometimes use f, g, h to name functions. Function names can also # clash. Python also allows a function to be defined locally inside another # function. def f(n): def g(x): return x*x # end of inner function g return g(n-1) # body of outer function f # end of function f # print g(2) # error: the function g is only visible inside the body of f.