LAB 4 - Subroutines

Functions and parameters

If we continue to write more involved programs and put all of our instructions in main, our programs will become much too long and complex to read and modify. In Python, we can write additional function definitions that perform some of the actions for us. Here is a short example to begin with. We might want to print the lyrics to a song like "Happy Birthday" for Alice as follows:

def main():
	print "Happy Birthday to you"
	print "Happy Birthday to you"
	print "Happy Birthday Alice"
	print "Happy Birthday to you"

main()

There is repetition here, but it is scattered. We repeat "Happy birthday to you" three times, but not one after the other, so we can't just write a loop to repeat this 3 times. We can write another function that plays the role of printing the message "Happy Birthday to you". Then we can just call this function three times at the appropriate times in main:

def main():
	printLyrics()

def printLyrics()
	happy()
	happy()
	print "Happy Birthday, dear Alice"
	happy()

def happy():
	print "Happy Birthday to you"

main()

Let's say we want to print the lyrics for both Alice and Bob. We can do this by using a parameter to the function. A parameter is a value that we pass into the function that it needs to use, in this case, the name of the person.

def main():
	printLyrics("Alice")
	print ""			# skip a line
	printLyrics("Bob")

def printLyrics(person):
	happy()
	happy()
	print "Happy Birthday, dear", person
	happy()

def happy():
	print "Happy Birthday to you"

main()

In the example above, the first time main calls printLyrics, it sends the string "Alice" over. The printLyrics stores "Alice" in person and then starts its computation. When it gets to its third line, person evaluates to Alice and is printed. The second time main calls printLyrics, it sends the string "Bob" over. The printLyrics stores "Bob" in person and then starts its computation. When it gets to its third line, person evaluates to Bob and is printed.

Here is the output:

Happy Birthday to you
Happy Birthday to you
Happy Birthday Alice
Happy Birthday to you

Happy Birthday to you
Happy Birthday to you
Happy Birthday Bob
Happy Birthday to you

One advantage of this type of programming is that if we require a change, such as converting the lyrics to Spanish, we don't have to change many lines, just a few:

def main():
	printLyrics("Alice")
	print ""			# skip a line
	printLyrics("Bob")

def printLyrics(person):
	happy()
	happy()
	print "Feliz cumpleanos a", person
	happy()

def happy():
	print "Feliz cumpleanos a ti"

main()

Functions with a return value

Recall that we wrote a program to convert from Fahrenheit to Celsius. Let's say we want to print out a table with the temperatures in Fahrenheit from 0 to 100 in steps of 10 along with their Celsius equivalents. First, we can write a function that converts from Fahrenheit to Celsius:

def f_to_c(tempF):
	tempC = (5.0/9.0) * (tempF - 32.0)
	return tempC

Notice above that there is one parameter for this function. The variable tempF represent the temperature in Fahrenheit. The function then uses that to compute the corresponding Celsius temperature. The last statement returns this result as its answer. Now we can write a program that uses this function, calling it with each Fahrenheit temperature from 0 to 100 in steps of 10:

def main():
        print "F", "\t", "C"
        for x in range(11):
                print x*10, "\t", f_to_c(x*10)

def f_to_c(tempF):
        tempC = (5.0/9.0) * (tempF - 32.0)
        return tempC       

main()

The loop in main runs 11 times, setting x = 0, then 1, then 2, ..., then finally 10. Each time we print out x * 10 (0, 10, 20, ..., 100) and then call the f_to_c function with the current value of x*10. Each time the main function calls f_to_c, the value of x*10 is stored in tempF for use in the f_to_c function. The function returns the Celsius temperature back to the main function. Since the value is returned back to a print statement, it is printed. We could also have written the code so the returned value is stored in a variable for later use:

def main():
        print "F", "\t", "C"
        for x in range(11):
		celsius_temp = f_to_c(x*10)
                print x*10, "\t", celsius_temp

def f_to_c(tempF):
        tempC = (5.0/9.0) * (tempF - 32.0)
        return tempC       

main()

Here is the output:

F 	C
0 	-17.7777777778
10 	-12.2222222222
20 	-6.66666666667
30 	-1.11111111111
40 	4.44444444444
50 	10.0
60 	15.5555555556
70 	21.1111111111
80 	26.6666666667
90 	32.2222222222
100 	37.7777777778

Note: To clean up the output, we can use the round function to round the Celsius temperatures to the nearest integer. Note that the round is similar to the functions we're writing, except that it is predefined for us. We call it with a floating point value and it returns the nearest integer as its result. For example, print round(42.63) would call the round function with the value 42.63 and it returns 43.0 as its result which we then print. Here is our revised program now:

def main():
        print "F", "\t", "C"
        for x in range(11):
                print x*10, "\t", round(f_to_c(x*10))

def f_to_c(tempF):
        tempC = (5.0/9.0) * (tempF - 32.0)
        return tempC

main()

Here is the output now:

F 	C
0 	-18.0
10 	-12.0
20 	-7.0
30 	-1.0
40 	4.0
50 	10.0
60 	16.0
70 	21.0
80 	27.0
90 	32.0
100 	38.0

Reading in data from the keyboard using a function

One final example for now: in our last lab, we wrote a program to find the maximum of an array of integers. The program had its limitations since it only worked on whatever array you typed in the program. It would be much better if we could program it so it finds the maximum of ANY array we want. We can do this by writing a function that asks the user at the keyboard to type in the data values for the array, rather than "hard-coding" it into the program itself. Here's how we do this:

def getData(n):
        numbers = []    # empty array
        for x in range(n):
                print "Enter value", x, ":",
                value = input()
                numbers.append(value)
        return numbers

The function requires a parameter we call n. This value represents how many numbers we want in our array. Next, we start with an empty array called numbers. We then loop n times, asking the user to input a value for the array. (We'll assume they're entering integers for now.) Each value that is input is appended to the end of the numbers array using the append function on the numbers array. Once the loop runs n times, we have all of our data, so we return the numbers array, now full, as our result.

This function can be used to modify our program to find the maximum as follows:

def main():

        numValues = input("Enter the number of data values to analyze: ")
        A = getData(numValues)
        i = 0
        max = A[0]
        while (i < len(A)-1):
                i = i + 1
                if A[i] > max:
                        max = A[i]
        print "The maximum value is ", max

def getData(n):
        numbers = []    # empty array
        for x in range(n):
                print "Enter value", x, ":",
                value = input()
                numbers.append(value)
        return numbers

main()

Looking at the main function (where the program begins), we first ask the user to input the number of values for the array. Let's assume they input a positive integer for now. We then call the function getData sending the value numValues to this function to use. The function stores the passed value into n and then runs as described above. The resulting array that is created is returned back to main, where main stores the result in the variable A. Now the rest of the program computes the maximum as we did before.

Here is a sample run:

Enter the number of data values to analyze: 5
Enter value 0 : 4
Enter value 1 : 8
Enter value 2 : 9
Enter value 3 : 5
Enter value 4 : 7
The maximum value is  9

Writing the program this way makes it more readable since each function has its own purpose. The getData is responsible for initializing the array, and the main function is responsible for computing the maximum in the array. We generally write additional functions in order to separate computational parts of our program into logical units and also to allow us to reuse code without having to cut and paste it over and over.