Exercise 1

Sometimes programmers write functions that are difficult to read. Let’s take a look at the following function.

def apple(orange, banana):
    """Given an orange and a banana, will return a pear"""
    pear, kiwi = [], 0
    while kiwi < len(orange) or kiwi < len(banana):
        mango = pear + [5]
        if kiwi < len(orange) and len(mango) > len(pear):
            pear.append(orange[kiwi])
        if kiwi < len(banana) or False:
            pear.append(banana[kiwi])
        kiwi = kiwi * 1 + (17 - 4*4)
    return pear

a. Determine what the function is doing and give it a more appropriate name.

b. The variable names aren’t very helpful. Give them names that are more descriptive of what they are.

c. Clean up the function by removing any unnecessary code that does not serve a purpose.

d. Write a more appropriate docstring for the function and include the following sections

  • A short one-line purpose statement
  • A Parameters section listing the types of the parameters
  • A Returns section briefly summarizing the return value and its type
  • A Preconditions section if there are more restrictions on the input other than their type
  • A Postconditions section that describes, in more detail, the exact relationship between the output and the inputs

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 in a previous lab. 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
    
    Preconditions:
       * len(lst) > 0
       * 0 <= index1 < len(lst)
       * 0 <= index2 < len(lst)"""
    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 one more elif case that checks if index1 and index2 are within the correct range of indices, that is, there are in the range between 0 (inclusive) and len(lst) (exclusive).

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.

Exercise 4

Write a function called grid_points(n, k) that takes two integers and returns a list of lists that consists of all the points [x,y] where 0 <= x < n and 0 <= y < k. For example,

>>> grid_points(2,3)
[[0, 0], [0, 1], [0, 2], [1, 0], [1, 1], [1, 2]]
>>> grid_points(1,1)
[[0, 0]]
>>> grid_points(5, 1)
[[0, 0], [1, 0], [2, 0], [3, 0], [4, 0]]

Your grid_points function should also raise a TypeError exception if one of the parameters is the wrong type and a ValueError if either parameter is less than or equal to zero. Be sure to give helpful error messages!

You should save your function in a file named grid_points.py and include a docstring description of it that has

  • A short one-line purpose statement
  • A Parameters section listing the types of the parameters
  • A Returns section briefly summarizing the return value and its type
  • A Preconditions section if there are more restrictions on the input other than their type
  • A Postconditions section that describes, in more detail, the exact relationship between the output and the inputs
  • A Raises section that describes what exceptions it raises and under what conditions