(Aim to complete this before your lab in week 5.)

Learning Objectives

      to gain practice in the use of while loops

      to gain practice in the use of functions

      to gain practice in the writing of functions

Make a new folder called Lab 3 in your Labs folder. All the example programs that you develop during this lab should be kept in this folder.                                                                         

Loops and Functions

Processing Exam Marks

Open an IDLE editor window and copy and paste the following program into it.

program to calculate average of a series of exam marks
created by sands 23/10/10

# initialise total and counter
total = 0.0
count = 0

print("Enter marks one per line")
print("Use a negative number to end")

# get first mark
mark = float(input("Mark: "))

# process mark and get next one
# continue until negative number entered
while mark >= 0 :
    total = total + mark
    count = count + 1
    mark = float(input("Mark: "))

# output average
if count == 0 :
   print("No marks entered")
else :
   print("The average mark is", round(total/count, 2))

This file is a slightly modified version of the program from slide 13 of part 3 to calculate the average of a series of exam marks.  Save the file as  average.py in your Lab 3 folder.

Observe that the displayed average has been rounded to 2 decimal places.

Run and test the program.

Modify the program so that it also calculates the minimum and maximum marks. You will need to use two new variables, mini and maxi, to keep track of the smallest and largest values entered so far. These should be initialised to the first mark entered, so this needs to be done after the initial input but before the loop. Inside the loop you will need to check whether the newly-entered mark is smaller than mini or larger than maxi, and if so, update the value of the appropriate variable - use if statements to do this. Add another print statement after the output of the average to output a message such as

  The largest mark was 82.5 and the smallest mark was 17.3

The output values should this time be rounded to one decimal place. Run and test.

Displaying Patterns

Copy and paste the following program into a new editor window.

program to display rows of characters
created by sands 23/10/10

def printRow(c, length) :
    print a row of identical characters
    c: string: character to be used
    length: int: length of row
    line = c * length

printRow('=', 10)
printRow('*', 7)
myChar = '+'
myLen = 9
printRow(myChar, myLen)

Save it as rows1.py.

This file contains a function to display a row of characters and some sample calls to the function.

The function contains the code to print a row; each time we want to print a row we need to make a call to the function.

Observe that the function definition uses the keyword def, followed by the name of the function, and in parentheses the names of the arguments to the function. This is followed by documentation for the function and the body of the function (i.e. the code to print a row) - these must be indented.

When the function is called the arguments are given the values supplied in the call and then the code in the function body is run: hence the first call gives the value '=' to the argument c and the value 10 to the argument length and then generates the output string and prints it.

Modify the program so that the user is asked to supply values for the variables myChar and myLen.

Test and run. What happens if the user types more than one character when asked to supply a value for myChar?

To ensure that only the first character is used if the user types more than one, you should modify the call to the printRow on the last line of the program so that the argument is myChar[0] instead of myChar.

Test and run, and again check what happens if the user types more than one character.

We now wish to display a triangular pattern of the form


Modify the documentation at the top of the program so that the filename is rows2.py and save the program as rows2.py.

The code already written can be used to allow the user to select the character to be used and the length of the first row; just modify the prompt so that it now asks the user for the length of the first row.

You will need to use a while loop to draw the rows. This should take the form

   while myLen>0 :

Inside the loop body you need to display a row (using a call to the printRow function) and then decrement myLen.

Run and test the program.

Add after the while loop, another while loop so that the program will generate output of the form


In the second while loop we need to increment the length and continue until it reaches to its initial value - hence you will need to save the initial value of myLen in an extra variable; this will of course need to be done before the first loop. Note that at the end of the first loop myLen will have reached 0 so its value will need to be adjusted before entering the second loop.

Run and test. Check that there is just one row containing one character. (If there is more than one row you will need to modify the value adjustment that was performed between the two loops.) When the program appears to be working correctly check what happens when the length of the first row is 1 or 0.

You will need to come back to the current version of the program in a later exercise so before proceeding use Save As... to change the program name to rows3.py to ensure that the next program does not get saved as rows2.py when you run it. Change the program name in the documentation string to rows3.py.

Copy and paste the following code into the program, immediately beneath the body of the printRow function

def printRec(c, rows, cols) :
    print a rectangle of identical characters
    arguments :
    c : str : character to be used
    rows : int : number of rows
    cols : int : number of columns

This function should use a loop to make several calls to printRow (c, cols)- the number of calls should be equal to the value of the argument rows. Add code to do this immediately beneath the documentation for the function, remembering that it must be indented by the same number of spaces as the documentation.

Replace the code that was used to generate the triangles with a call of the form

   printRec('*', 4, 7)

Run; you should see output of the form


Add code to ask the user to select the character to be used and the number of rows and columns, store the input values in variables and use these variables as the arguments in the call to printRec.

Run and test.

Save as rows4.py and add to the program, between the printRow and printRec functions, a new function

   def printAlternatingRow(c1, c2, length)

The body of this function should be similar to that of printRow but should generate output of the form


with alternating occurrences of the two characters supplied as arguments.

There are several ways that this could be done. The simplest is probably to first generate a string containing the two characters (using something like myStr = c1 + c2, then multiply this by length//2. We now have a string containing an even number of characters - if the value of length was even it would contain the string we need to output, but if the value was odd an extra copy of c1 would need to be appended to the end. (Use a statement of the form if length%2==1 : .... to do this.)  Having generated the string it must of course be output.

Modify the code that performs input so that it now asks the user for two characters and a length and uses the values supplied by the user as arguments to a call to the new function.

Run and check that your program works correctly with both odd and even numbers supplied for the length.

Modify the printRec function so that it takes an extra character argument and uses printAlternatingRow instead of printRow and modify the input section so that it asks the user for the number of rows and columns instead of a length and uses the input values as arguments to a call to printRec instead of the call to printAlternatingRow.

You should now see output like


In a new version of the program called rows5.py modify the printRec function so that the output looks like


(Hint: you will need to check whether the current row number is odd or even and supply the arguments to the call to printAlternatingRow in a different order in each case.)

Run, testing with several different inputs, then close the editor window.

Make a new copy of your rows2.py file - call this rows6.py and open it in IDLE.

Modify the printRow function so it takes four arguments: two characters and two integers.

Modify the function body so that it generates output of the form


The output should be symmetric so the first integer argument should be used to specify how many characters are output at the beginning and end of the line and the last argument should be used to specify how many characters are output in the middle.

The code beneath the function is no longer appropriate for the new version.  We will shortly wish to modify it, so we do not want to delete it.  However we need to test that the new method works correctly before continuing. Hence you should temporarily comment out this code by highlighting it in the editor window and then using Alt-3 (i.e. hold down the Alt key and press the 3 key).

Run the program - it will produce no output since there are no calls to the function. However the function definition will have been loaded into the shell window so you can check that it works by typing in that window calls such as

   printRow('+', '=', 3, 7)

which should generate output like that seen above.

Try several other calls until you are confident that the function works correctly.

Now select the commented-out code in the editor window and use Alt-4 to bring it back into the program.

Our aim is to produce output looking like


Modify the input code so that the user is now asked to input two characters and one integer. The integer will be the length of the first sequence of characters on the first row (e.g. 7 for the above example).

Modify the calls to printRow so that they have four arguments, the two characters input by the user, the length value as used in the previous version and (temporarily) 1.

Run the program and supply as inputs  +, * and 5. The output should look like


We need to modify the last argument to the calls to printRow. Declare a new variable, whose initial value is 1 and use it as the last argument to all of the calls to printRow. This variable will need to be incremented by 2 after the call to printRow in the first loop and decremented by 2 after the call in the second loop. As in the earlier exercise an adjustment to its value will be needed between the two loops; if you cannot work out exactly what adjustment will be needed you can use "trial and error".

Functions that Return Results

Functions may return results to their callers; we have already encountered several such functions: input obtains a line of text from the user and returns it to (in all cases where we have used it) the caller; we have normally stored the result in a variable using an assignment statement such as

   firstName = input("First name: ")

The result of a function may be used anywhere that an expression of the appropriate type is expected. We have often used statements such as

   age = int(input("Age: "))

Here the result returned by the input function is passed as an argument to the age function and the result returned by the latter is stored in the variable.

It is legitimate to write statements such as

   print("Hello", input("First name: "))

Try typing the above statement in the shell window. Note that the prompt for the name appears before Hello is printed; the arguments of a function are always evaluated before the function starts to run.

Also try typing in the shell window

   printRow(input("First char: "), input("Second char: "), 7, 6)

Within the body of a function we specify the result that is to be returned using a return statement:

   return val

A function may have more than one return statement, e.g.

  if (...) : return 1
  else : return 0

Calculating Powers

Copy and paste the following code into an editor window.

Calculates powers without using **
created by sands 24/10/10

def power(a, n) :
    calculates powers
    arguments: a : int, n : int
    returns: a to the power n : int
    result = 1

    if n<0 :
        print("Warning - negative powers not integers - returning 0")
        return 0
    else :
        # need to do some arithmetic in a loop here
        return result

# some calls to test the function
print("2 to the power 3 is ", power(2,3));
print("3 to the power 4 is ", power(3,4));
print("4 to the power 5 is ", power(4,5));

Save it as pow1.py in your Lab 3 folder and rename it in the usual way. This file contains an incomplete function to calculate and return the value of an. (a0 is defined to be 1, a1 is a, a2 is a*a, a3 is a*a*a, a4 is a*a*a*a etc) and some calls to test it. (We could, of course, obtain the result using the ** operator, but languages like Java do not have such an operator so it is useful to know how to calculate a power without using it.)

Examine and run the program. You will see that the result is always 1 - the function does not do any multiplication!

Modify the function so that the function returns the correct result (without using **) - you will need to add a loop in order to perform the assignment

   result = result * a;

n times.

Run and check that your program works correctly - 23 should be 8, 34 should be 81, 45 should be 1024.

Modify the calls at the end of the program function so that the user is asked to input the values for a and n. Run and test, supplying as arguments, for example, 7 and 8 or 8 and 9.

Modify the program so that it expects the user to type a real number instead of an integer for the value of a and use it to calculate, for example 2.753 and 10.510. Note that even though the documentation says that the function expects an integer for the first argument it will happily accept a real number.

Optional Additional Exercises

Exercise 1

Make in your Lab 3 folder a copy of your euro.py program from lab 1 (or use my solution if you didn't get it working correctly).

Open the file in IDLE and modify it so that it performs the inputs in a loop, asking for Cost of item 1, Cost of item 2 etc. (the structure of the loop should be similar to that of the average-calculation program). The program should continue to accept inputs until the user enters a cost of 0, at which point it should display the total cost in both euros and pounds and pence.

Exercises 2-3

In each of the following exercises each function that you write should contain documentation in the style used in the examples already seen.

Exercise 2

Write, in a new program, a function that takes one integer argument and calculates and returns the factorial of that integer.  Beneath the function write a loop that allows the user to input numbers, calculates and displays the factorial of each number entered, and terminates when the user enters a negative number.

[ The factorial of n, written by mathematicians as n!, is the product of all the numbers from 1 to n, so 5! is 54321. ]

Exercise 3

Write a program that contains a function that takes as an argument a string and returns the number of vowels contained in that string. The main body of the program should ask the user to supply a line of text, pass the input string as an argument to the function and display the result returned by the function.

[ Hints: You will need to loop through the characters in the string until you reach the end, incrementing the result whenever the character is a vowel. To control the loop you will need a variable to keep track of how many characters have been checked and stop when this is equal to the length of the string. The length of a string s is obtained using len(s) .To determine whether the character at location n in the string s is a vowel you can use the expression s[n] in "AEIOUaeiou" as the condition for an if statement - this has a boolean value which will be True if the character occurs in the string "AEIOUaeiou"; don't forget that the first character in the input string is at location 0. ]