Preparation

a. Make sure you are booted into the Mac OSX side of the computer.

b. Once you are logged in, double click the icon named courses.ads.carleton.edu on your desktop. If you encounter any errors, have me or the prefect contact Mike Tie to set up your account.

c. Open up a Terminal, navigate to your StuWork directory, create a folder named lab-02-04, and change your directory to the newly created folder. (Remember that you should replace USERNAME with your actual username!)

$ cd /Volumes/courses/cs111-01-w19/StuWork/USERNAME/
$ mkdir lab-02-04
$ cd lab-02-04

d. Open Brackets and make sure you have your StuWork/USERNAME directory opened. (You will see all of its subfolders in the left panel if so.) If the folder is not opened, you can open it by selecting File -> Open Folder and navigating to courses, then cs111-01-w19, then StuWork, then USERNAME.

Exercise 1

In the testing lab, you were asked to write a function called reverse_list that reverses a list via a side effect. Here is an attempted solution to that exercise using a helper function named swap:

def swap(lst, index1, index2):
    """Modifies the list so that the elements at index1 and index2 are swapped

    Parameters:
        lst: A list
        index1: An integer
        index2: An integer

    Returns:
        Nothing; called for the side effect

    Preconditions:
        * len(lst) > 0
        * 0 <= index1 < len(lst)
        * 0 <= index2 < len(lst)

    Postconditions:
        Let old_lst refer to the list before executing the function. Then
        after executing this function:
         * len(lst) = len(old_lst)
         * lst[index1] = old_lst[index2]
         * lst[index2] = old_lst[index1]
         * for all other indices, i,
             lst[i] = old_lst[i]
    """
    lst[index1] = lst[index2]
    lst[index2] = lst[index1]

def reverse_list(lst):
    """Reverses the order of the elements of the list lst

    Parameters:
        lst: A list

    Returns:
        Nothing; called for the side effect

    Postconditions:
        Let old_lst refer to the list before executing the function. Then
        after executing this function:
         * len(lst) = len(old_lst)
         * for all reasonable i, lst[i] = old_lst[len(old_lst) - 1 - i]
    """

    # Iterates over list and swaps the elements as follows:
    #   index 0 gets swapped with len(lst) - 1
    #   index 1 gets swapped with len(lst) - 2
    #   index 2 gets swapped with len(lst) - 3
    #   and so on...
    for i in range(len(lst)):
        swap(lst, i, len(lst)-1-i)

a. Save the above code in a file named reverse.py and import both functions into the REPL.

>>> from reverse import *

b. Try executing reverse_list on the following list to see if it works properly.

>>> test_list = [100,200,300,400,500]
>>> reverse_list(test_list)
>>> test_list

c. OK, so it is indeed broken. Try executing it on a couple more lists; then discuss with your partner and make a hypothesis on where the most likely place the bug exists.

d. We will use pudb3 to investigate our hypothesis. To start, add the following main function to your code and feel free to change the test_list to one that may be more appropriate for testing your hypothesis.

def main():
    test_list = [100,200,300,400,500]
    reverse_list(test_list)
    print(test_list)

if __name__ == "__main__":
    main()

e. Execute the code with pudb3 to start up the Python debugger.

$ pudb3 reverse.py

f. Before we start debugging, there are a couple useful preferences to change. If pudb3 initially opens up the options menu, make sure to make the following changes.

  • Make sure Show Line Numbers is checked
  • Under Variable Stringifier, make sure that repr is checked

If the preferences did not open initially, you can press Ctrl + P to open them.

g. To step through your code line by line, type the n key to execute the next line of code. Note that n will not enter into a function, but rather it executes the whole function at once. If you’d like to step into a function call, press the s key. (At the top of the debugger, there is a helpful reference that reminds you what the keys are in case you forget.)

h. Let’s try investigating using breakpoints. Navigate to line swap(lst, i, len(lst)-1-i) and press b to set a breakpoint. Now, to continue executing your program until a breakpoint is pressed, you can press the c key. Try pressing it and watch how the list is updated when the swap function is called.

i. Try stepping into the swap function and examine what it does. Does it successfully swap the two values? If not, discuss with your partner why the values are not being swapped properly.

j. Exit out of pudb3 by pressing q and fix the swap function so that it properly swaps values. Test it in the REPL to make sure that it is working properly.

k. After fixing the swap function, try testing the reverse_list function one more time. Does it work?

l. If the only bug you fixed was in the swap function, you’ll find that the reverse_list function still does not work. Discuss with your partner what you think is happening, and investigate your hypothesis using the debugger.

m. Fix the error in the reverse_list function and make sure it is working properly by running a few tests.

Exercise 2

It is common to verify that the preconditions of your code are met and raise an exception if not. We will do this on the swap function defined above. Below I have checked that the parameters have the appropriate type and raised an exception otherwise:

def swap(lst, index1, index2):
    """Modifies the list so that the elements at index1 and index2 are swapped

    Parameters:
        lst: A list
        index1: An integer
        index2: An integer

    Returns:
        Nothing; called for the side effect

    Preconditions:
        * len(lst) > 0
        * 0 <= index1 < len(lst)
        * 0 <= index2 < len(lst)

    Postconditions:
        Let old_lst refer to the list before executing the function. Then
        after executing this function:
         * len(lst) = len(old_lst)
         * lst[index1] = old_lst[index2]
         * lst[index2] = old_lst[index1]
         * for all other indices, i,
             lst[i] = old_lst[i]
    """
    if type(lst) != list:
        raise TypeError("first arg must be a list")
    elif type(index1) != int:
        raise TypeError("second arg must be an int")
    elif type(index2) != int:
        raise TypeError("third arg must be an int")

    temp = lst[index1]
    lst[index1] = lst[index2]
    lst[index2] = temp

a. Add another elif case that checks if the len(lst) > 0 precondition is satisfied and raises a ValueError exception with an appropriate message if not.

b. Add two more elif cases that check the last two preconditions and either raises a ValueError or an IndexError with an appropriate message if not. (Both ValueError and IndexError are appropriate exceptions since passing in invalid indices causes swap to break.)

Exercise 3

Write a program called sqrt.py that asks the user to enter a number and prints the square root of that number. However, instead of crashing with a TypeError if the user enters a non-number, your program should handle the error by displaying a helpful error message and asking them to retype the number.

For Those With Extra Time

Start working on the assignment.