Python: debugging, more loops

List operations

We ended last time looking at a couple of functions that use for-loops:

def sum(lst: list) -> int:
    """returns the sum of a list of numbers"""
    running_sum = 0
    for n in lst:
        running_sum = running_sum + n
    return running_sum


# courses_in_dept(["CSCI0111"], "CSCI") == ["CSCI0111"]
def courses_in_dept(courses: list, dept: str) -> list:
    """returns a list of courses whose names start with dept"""
    found_courses = []
    for course in courses:
        if course.startswith(dept):
            found_courses.append(course)
    return found_courses

In Pyret, we wrote list processing functions using both cases expressions (which, as we’ve seen,, we will replace with for-loops when we write Python code) and the built-in list operations such as filter, map, etc. Python also has built-in list operations; for example, the above loop could be re-written as a filter expression:

def courses_in_dept(courses: list, dept: str) -> list:
    """returns a list of courses whose names start with dept"""
    return list(filter(lambda course: course.startswith(dept), courses))

Things to notice here:

  • Python has lambda-expressions, just like Pyret does. The syntax is slightly different, but they are doing the same thing.
  • We need to call list on the result of filter–we won’t talk about why in this course.

Debugging

Let’s write one more function that uses a loop:

def longest_string(strings: list) -> str:
    """return the longest of a list of strings"""
    longest = ""
    for s in strings:
        if len(s) >= longest:
            longest = s
    return longest

Let’s take a look at how this function works:

> longest_string(["cat", "or", "dog"])
"dog"

Imagine we are confused about why the function is returning "dog" instead of "cat". In order to figure it out, we need to understand how the function executes. We could simulate its execution on paper, on a whiteboard, or in our heads. Or, we could ask the computer to help us!

Print statements

One way to debug a function like this is to insert print statements:

def longest_string(strings: list) -> str:
    """return the longest of a list of strings"""
    longest = ""
    for s in strings:
        print(s)
        if len(s) >= longest:
            longest = s
    return longest

If we execute the function now, we see something like:

> longest_string(["cat", "or", "dog"])
cat
or
dog
"dog"

We could insert another print statement:

def longest_string(strings: list) -> str:
    """return the longest of a list of strings"""
    longest = ""
    for s in strings:
        print(s)
        if len(s) >= longest:
            print(s)
            longest = s
    return longest
> longest_string(["cat", "or", "dog"])
cat
cat
or
dog
dog
"dog"

We could even add some formatting:

def longest_string(strings: list) -> str:
    """return the longest of a list of strings"""
    longest = ""
    for s in strings:
        print("Looking at " + s)
        if len(s) >= longest:
            print(s + " is longer than " + longest)
            longest = s
    return longest
> longest_string(["cat", "or", "dog"])
Looking at cat
cat is longer than
Looking at or
Looking at dog
dog is longer than cat
"dog"

This should give us enough information to diagnose the problem.

Debugging by stepping through the program

Another option is to use the step-through debugger. We can add to our file:

print(longest_string(["cat", "or", "dog"]))

The lecture capture demonstrates:

  • starting the step-through debugger
  • seeing how variable values change
  • using the debugger console

See the lecture capture for details on how the step-through debugger works.

What are the pros and cons of print statements vs. the step-through debugger?

Another loop example

Let’s say we want to write a function that takes a list of strings and counts the total number of “z”’s that appear. How could we do this?

def zs_in_list(lst: list) -> int:
    count = 0
    for s in lst:
        print(s)
        for c in s:
            print(c)
            if c == 'z':
                count = count + 1
    return count