Today: a bit of grab-bag, debugging, list functions, boolean precedence

Midterm

Midterm - Fri Feb 14th, at regular class time. Talk about next week. What to know now:

Midterm is closed-note, writing functions. The functions look a lot like functions from homework/section/lecture - be able to do those. Re-visit things you have solved before, maybe with help. Practice to be able to solve them cold. We give partial credit and do not grade on syntax.

See also: Midterm Prep

Last quarter's midterm: Midterm

Grayscale

Thinking About Bugs

See Python Debug

alt:python bug then symptom

Thinking About Tests

1. Do Tests Prove A Function is Correct?

No!

Each test is like drilling for oil. We drill five holes, and find no oil in all of them. Does that mean there is no oil? Probably there is no oil, but we can't say for sure. Each test is looking for a bug. As a practical matter, even just a few tests do a great job finding bugs. You do not need a zillion tests, and probably your functions in CS106A bear this out - when you have 5 tests .. that's often plenty.

2. Debugging Technique 1 - Look at Got

3. Temporarily Add print() - Debugging

CrazyCat Print Example

Add print() inside loop of crazy_line() 2x below

def crazy_line(line):
    """
    Given a line of text, returns a "crazy" version of that line,
    where upper/lower case have all been swapped, so 'Hello'
    returns 'hELLO'.
    >>> crazy_line('Hello')
    'hELLO'
    >>> crazy_line('@xYz!')
    '@XyZ!'
    >>> crazy_line('')
    ''
    """
    result = ''
    for i in range(len(line)):
        char = line[i]
        if char.islower():
            print('lower:', char)       # ADDED
            result += char.upper()
        else:
            print('not lower:', char)   # ADDED
            result += char.lower()
    return result

Now this is what the Doctest run looks like - see the print lines before the got. Showing us what values where in the vars in the loop.

Failed example:
    crazy_line('Hello')
Expected:
    'hELLO'
Got:
    not lower: 0 H
    lower: 1 e
    lower: 2 l
    lower: 3 l
    lower: 4 o
    'hELLO'

Just looking at your code and the got is the first step. This is a fallback.

Print() With Online Problems

This print() technique works with the online problems too. You'll see the print() lines below the got. Demo this in a bit.


More List Functions

More details see guide: Python Lists

List 1.0 Features

>>> nums = []
>>> nums.append(1)
>>> nums.append(0)
>>> nums.append(6)
>>> 
>>> nums
[1, 0, 6]
>>> 
>>> 6 in nums
True
>>> 5 in nums
False
>>> 5 not in nums
True
>>> 
>>> nums[0]
1
>>> 
>>> for num in nums:
...   print(num)
... 
1
0
6

List Slices

>>> lst = ['a', 'b', 'c']
>>> lst2 = lst[1:]   # slice without first elem
>>> lst2
['b', 'c']
>>> lst
['a', 'b', 'c']
>>> lst3 = lst[:]    # copy whole list
>>> lst3
['a', 'b', 'c']
>>> # can prove lst3 is a copy, modify lst
>>> lst[0] = 'xxx'
>>> lst
['xxx', 'b', 'c']
>>> lst3
['a', 'b', 'c']

lst.extend(lst2)

>>> a = [1, 2, 3]
>>> b = [4, 5]
>>> a.append(b)
>>> # Q1 What is a now?
>>>
>>>
>>>
>>> a
[1, 2, 3, [4, 5]]
>>>
>>> c = [1, 3, 5]
>>> d = [2, 4]
>>> c.extend(d)
>>> # Q2 What is c now?
>>>
>>>
>>>
>>> c
[1, 3, 5, 2, 4]

Alternative: lst1 + lst2

>>> a = [1, 2, 3]
>>> b = [9, 10]
>>> a + b
[1, 2, 3, 9, 10]
>>> a   # original is still there
[1, 2, 3]

Like extend(): lst1 += lst2

>>> a = [1, 2, 3]
>>> b = [4, 5]
>>> a += b
>>> a
[1, 2, 3, 4, 5]

lst.pop([optional index])

over so they are all still indexed 0..len(lst)-1
>>> lst = ['a', 'b', 'c']
['a', 'b', 'c']
>>> lst.pop()   # opposite of append
'c'
>>> lst
['a', 'b']
>>> lst.pop(0)  # can specify index
'a'
>>> lst
['b']

pop() vs. slice

lst.insert(index, elem)

>>> lst = ['a', 'b']
>>> lst.insert(0, 'z')
>>> lst
['z', 'a', 'b']

lst.remove(target)

>>> lst = ['a', 'b', 'c', 'b']
>>> lst.remove('b')
>>> lst
['a', 'c', 'b']

Example Problems

Added combine() (extend) and rotate() (pop) functions below. Try one or two of these.

> list2 examples

Temporarily add print(lst) in the loop - debug technique works in here.


Boolean Operators

Boolean Precedence

This does not work, but it's close

def good_day(age, weekend, raining):
    """age is int, weekend and raining are Boolean"""
    if age < 30 or weekend and not raining:
        print('good day')

Solution

def good_day(age, weekend, raining):
    """age is int, weekend and raining are Boolean"""
    if (age < 30 or weekend) and not raining:
        print('good day')

Parse Example - parse_words99()

Parse problems from last time

> parse examples

Later problem has boolean precedence in it.

parse_words99: Like parse_words(), but with an extra way for a word to extend. Given a string s, parse out and return all "words", where a word is made of 1 or more adjacent alphabetic chars. Except, digits are allowed within a word after the first char, so 'ab12 6 a34a' returns ['ab12', 'a34b'].

Function Call - Parameters in Detail

(may get to this)

How do function call parameters work? Most often, a function call works intuitively, so you don't have to think about it too much. BUT when pushed, you need to know how it works to avoid getting tripped up.

1. The variables and parameters in each function are independent, sealed off from those in other functions. An "x" in one function is totally independent of an "x" in some other function.

2. Function call: parameter values come in by position within the ( .. ) (not by name or anything else)

Function Call Example

Say we have a "foo" function with 2 parameters, and it is called by a "caller" function later. What does the run of caller() below print? This is very detail oriented, but mercifully there's just a few lines.

def foo(x, y):
    x = x - y
    return x + 1


def caller():
    x = 2
    y = 3
    z = foo(y, x)
    print(x, y, z)

Solution

printed line:
2 3 2

Values passed in to foo(x, y) are 3 and 2, value returned is 1
The "x" within foo() is independent of the "x" in caller()

One More Thing

puzzle-crypt.txt