Week 5: more functions, scope, stack diagrams

Announcements

  • Quiz 2 on Friday

  • Lab 4 due Saturday

    • lab 4 is functions and while loops, but we tell you exactly how to write the functions (what the params are, what it does, what it returns)

    • lab 5 will be more functions, but not as much guidance on each function

    • lab 7 will be yours to write (you design the functions and the whole program)

Monday

We started by looking at the countletter.py program from last time:

def main():
    phrase = "we love computer science"
    letter = "e"
    result = countletter(letter,phrase)
    print("Number of %s's: %d" % (letter, result))

def countletter(ch,txt):
    """return number of ch in txt"""
    count = 0
    for char in txt:
        if char == ch:
            count += 1
    return count

main()

Some questions students had from last time: - how do letter and phrase get transfered to the countletter() function? - how does the result (the count variable) get back to main()? - should I name the arguments the same as the parameters? - can I use phrase in the countletter() function?

stack diagrams and scope

All good questions! And we’ll use stack diagrams to understand what’s going on and answer these questions.

Understanding stack diagrams helps you see how the computer works, and why python does what it does. It will also be very valuable in week 11 when we learn recursion (functions calling multiple copies of themselves).

A "stack" is just a data structure used by many programs. A stack is useful because it keeps track of where we are, and allows backtracking (e.g., hitting "undo" in your editor, or hitting the "back" button on your browser).

If you think of a stack of books or a stack of trays, you generally add items to the stack and remove items from the stack at the top end. This is different from a queue data structure (like a line at the bank or the grocery store) where items (people) are added at the back of the queue and removed (they checkout or are served) from the front of the queue.

So for your browser, say you visit espn.com, then swarthmore.edu, then cnn.com, and finally google.com. At this point, the broswer stack would look like this, with the current site on the top of the stack, and your history recorded in the rest of the stack:

google.com
cnn.com
swarthmore.edu
espn.com

If you now hit the "back" button, the browser would "pop" the stack and remove the top item (google.com) and go to whatever is next on the stack (cnn.com).

As functions are executed in our program, they are placed on the top of the stack. When the function executing is done, it is removed from the top of the stack and the function below resumes execution. This is how the computer keeps track of the program flow.

For example, if you have a main() function called first, and then main() calls another function, say func1(), and before func1() returns it calls another function, say func2(), then at this point "the stack" would look like this:

func2
func1
main

In addition to just showing what functions are on the stack, we will also include their variables and what data they refer to.

Here is a stack diagram for the countletter.py program above, where I have stopped at the end of the count() function, just before it returns to main():

stack diagram for countletter

In the above diagram, the "stack" is on the left, with two functions currently on the stack, and the one on top is the function currently executing (i.e., main() has paused, and is waiting for countletter() to finish and return).

In each stack frame, for each function, we list the variables that are active, with arrows that point to the data they refer to. This data, shown off to the right, is technically stored in a different area of the computer’s memory (this whole diagram is a simplified view of the memory of the computer, as the program is executing).

So main() has just two variables: phrase and letter. The arrows show what values are currently stored in these variables.

The countletter() function has three variables: ch, txt, and count. Currently ch points to the same data as letter. This is how arguments and parameters pass data from main() to a function. Also, txt points to the same string as phrase.

The count variable initially stores a zero, but as the computer works through the function, step by step, it is updated each time another "e" is found in the txt string. This is shown by the crossed-out values, with the final value being 5. So at the end of the countletter function, the value that will be returned back to main (count) is 5.

So the rules for drawing the stack are as follows:

  • put the first function called (usually main()) on the stack

  • inside the stack frame, show any variables that are assigned, with arrows pointing to their values (off to the right)

  • if another function is called, make a new stack frame for that function on top of the stack

  • again, inside that frame, show any parameters and local variables, with arrows pointing to their initial values

  • work through each function, step-by-step, updating any variables and their data

Usually, on quizzes, we will ask you to stop updating the stack diagram before any functions finish and return. For completeness, if a function did finish and return, we would remove it (and all it’s variables) from the top of the stack. Any return value would usually be assigned back to a variable in the calling function. For example, in the above program, back in main(), the result variable would be assigned the value 5, since count is returned from the countletter() function.

mutable objects in function

What about this program? Can you draw the stack for this one? And what does the print(data) line show, after the call to bounds() in main()?

def main():
  data = [2,37,300,127,-1]
  print(data)
  changed = bounds(data,0,255)
  print(data)
  print("Number changed: %d" % (changed))

def bounds(L,min,max):
  """check that all items in list are within min/max"""

  numchanged = 0

  for i in range(len(L)):
    if L[i] < min:
      L[i] = min
      numchanged += 1
    elif L[i] > max:
      L[i] = max
      numchanged += 1

  # after loop, return how many were changed
  return numchanged

main()

Wednesday

review function worksheet

Here’s the functionWorksheet.py program that we started as a worksheet on Monday. Can you predict the output of the whole program? Can you draw the stack up to where function2() is about to return?

# ------------------------------------------------------ #
def function1(L, X, S):
  X = 100
  L = list("WXYZ")
  S = "pony"
  # draw what stack looks like at this point, before function ends
# ------------------------------------------------------ #
def printline(ch):
  print(50*ch)
# ------------------------------------------------------ #
def function2(L, X, S):
  X = X + 1
  L[0] = "hello"
  L[2] = "goodbye"
  L[4] = "lists are mutable!"
  S = S + "pony"
  # draw what stack looks like at this point, before function ends
# ------------------------------------------------------ #
def function3(mystring):
  newstr = "**" + mystring + "**"
  return newstr
# ------------------------------------------------------ #
def main():

  L = list("ABCDE")
  X = 42
  S = "Swarthmore"
  function1(L, X, S)
  print("L: %s" % (L))
  print("X: %d" % (X))
  print("S: %s" % (S))

  result = printline("!")
  print(result)
  printline("!")

  function2(L, X, S)
  print("L: %s" % (L))
  print("X: %d" % (X))
  print("S: %s" % (S))

  printline("#")
  S = function3(S)
  print("S: %s" % (S))

# ------------------------------------------------------ #
main()

After you have studied the program, run it and make sure you understand the output!

Here are the main points from the above program:

  • in python, lists are mutable. This is why function2() is able to change some of the items in L, and those changes are seen back in main() (when we print(L))

  • scope refers to where in a program a variable is active or accessible. Variables (and the data they reference) in one function are local (ie, not global) to that function. If you need the data from one function in another function, you should pass the data from function to function using arguments and parameters, or return the data from the called function back to the calling function

  • in python, a function returns None if you don’t explicitly return something (which is why you see None from the print(result) line in main()

start the mathquiz.py program

Let’s write a program to present the user with a simple math quiz, testing their multiplication knowledge. Here’s a quick example:

$ python3 mathquiz.py

Welcome to MathQuiz v0.1!

What factor would you like to work on? 4
- - - - - - - - - - - - - -
4 x 6? 35
....Nope. 4 x 6 = 24
- - - - - - - - - - - - - -
4 x 4? 16
....Correct!
- - - - - - - - - - - - - -
4 x 10? 40
....Correct!
- - - - - - - - - - - - - -
4 x 2? 8
....Correct!
- - - - - - - - - - - - - -

You got 3 out of 4 correct. Good work!

Start with just these requirements:

  • ask for a factor

  • keep asking simple multiplication questions (factor times a random number from 1-12) until they get 3 in a row correct

If you have time: add a results(numprob, numcorrect) function that takes two arguments/parameters: the total number of problems asked and the total number they got correct. This function should output a status message based on the percent correct:

  • All correct: Super!

  • greater than 80% correct: Good work!

  • greater than 50% correct: Not bad…​

  • less that 50% correct: Keep trying…​

This function should also print the "You got X out of Y correct" message.

Friday

  • Quiz 2 for the first half of class

  • review mathquiz.py program (see jk_mathquiz.py program)

  • intro to objects: strings and lists

objects and methods

Both python strings and python lists are examples of objects. An object is just some data plus some methods (similar to functions) wrapped up together into one thing. We’ll learn more about objects as we go. For now we just want to learn the syntax of using them: object.method()

example

>>> course = "Introduction to Computer Science"
>>> course.count("o")
4
>>> course.count("z")
0
>>> course.isalpha()
False
>>> course.upper()
'INTRODUCTION TO COMPUTER SCIENCE'
>>> grades = [88,90,78,99,93]
>>> grades.sort()
>>> print(grades)
[78, 88, 90, 93, 99]
>>> grades.append(75)
>>> print(grades)
[78, 88, 90, 93, 99, 75]

explanation/notes

The string methods used above are:

  • count() — returns the number of occurrences of the given substring

  • isalpha() — returns True if all characters in the string are letters

  • upper() — returns an uppercase version of the string

There are many other str methods. In string objects, the data are the characters in the string.

The list methods used above are:

  • sort() — sorts the list in place

  • append() — adds the given argument to the end of the list

In list objects, the data are the items in the list.

object.method() syntax

Methods are always called with the dot syntax: object.method()

Again, methods are similar to functions, except they are tied to the type of object, and are called with this special syntax.

common string methods

Strings are objects in Python, and thus have methods that we can invoke on them. There are a lot of methods in the str library for creating new string objects from existing string objects and for testing properties of strings. Keep in mind that strings are immutable!

Here are a few str methods that may be particularly useful (run help(str) in the python interpreter to see the full set):

str method result

upper()

return copy of str converted to uppercase

lower()

return copy of str converted to lowercase

isalpha()

return True if string is all alphabetic characters

isdigit()

return True if string is all digits

count(sub)

return number of occurrences of sub

index(sub)

return index of first occurrence of sub

strip()

strip off leading and trailing whitespace

split()

split into list of "words" (see below)

>>> S = "we LOVE cs"
>>> S.upper()
'WE LOVE CS'
>>> S.lower()
'we love cs'
>>> S.isalpha()
False
>>> S.isdigit()
False
>>> S.count(" ")
2
>>> S.index("L")
3
>>> S.split()
['we', 'LOVE', 'cs']
>>> S = "   we love cs    "
>>> len(S)
17
>>> S = S.strip()
>>> len(S)
10
>>> print(S)
we love cs

common list methods

Lists are also objects in Python, and thus have methods that we can invoke on them. Here are a few that may be particularly useful (run help(list) in the python interpreter to see the full set):

list method result

append(item)

add item to end of list

insert(index,item)

insert item at index

extend(L1)

add list L1 to original list

sort()

sort the list

reverse()

reverse the list

count(item)

return number of occurrences of item in list

index(item)

return index of first occurrence of item

pop(index)

remove and return item at index

>>> L = list("ABCDEFG")
>>> print(L)
['A', 'B', 'C', 'D', 'E', 'F', 'G']
>>> L.append("X")
>>> print(L)
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'X']
>>> L.extend(["Y","Z"])
>>> print(L)
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'X', 'Y', 'Z']
>>> L.reverse()
>>> print(L)
['Z', 'Y', 'X', 'G', 'F', 'E', 'D', 'C', 'B', 'A']
>>> L.sort()
>>> print(L)
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'X', 'Y', 'Z']
>>> L.count("E")
1
>>> L.index("E")
4
>>> L.pop(4)
'E'
>>> print(L)
['A', 'B', 'C', 'D', 'F', 'G', 'X', 'Y', 'Z']
>>> L.insert(1,"hello")
>>> print(L)
['A', 'hello', 'B', 'C', 'D', 'F', 'G', 'X', 'Y', 'Z']

To see the full documentation for both the str and list classes:

$ python3
>>> help(str)
>>> help(list)