1. String slicing and indexing: what are the results of the following Python commands? (1 point each)
S1 = "Interesting stuff"
  1. S1[0] answer: "I"
  2. S1[-1] answer: "f"
  3. S1[:len(S1)] answer: "Interesting stuff"

rubric: this one should be pretty straightforward. Quotation marks not required.

  1. List slicing, indexing, and manipulation: what are the results of the following Python commands? (1 point each)
L1 = [[1,2,3],[],[4,5]]
  1. L1[1:] answer: [[],[4,5]]
  2. L1[1][0] answer: error (because we are trying to access position 0 (the first element) of an empty list, [])
  3. L1 + 2 answer: error (because we can only append lists to lists, not numbers to lists)
  4. L1 + [2] answer: [[1,2,3],[],[4,5],2]
  5. L1 + [[2]] answer: [[1,2,3],[],[4,5],[2]]

rubric: also straightforward. Don’t need to say exactly why/what the error is, just that the result is an error.

  1. (4 points) You should answer your phone on weekdays if it’s between 7 AM and 9 PM (time is measured in 24-hour time (0-23.99), so this means between 7 and 21); on weekends if it’s between 10 AM and midnight; and always if it’s your mom. If time is a numeric value between 0 and 24 (inclusive) and is_weekday and is_mom are logical (Boolean) values, write a function that begins def answer_phone(hour,is_weekday,is_mom): and returns the correct logical value. answer: there are a lot of ways to do this; one is
def answer_phone(hour,is_weekday,is_mom):
    return(is_mom or
           (is_weekday and 7 <= hour <= 21) or
           (not is_weekday and 10 <= hour <= 24))

rubric: -1 per mistake/failed case; -1 for not writing a function or failing to return a value. Best to return a true logical (True/False) value, but no deductions for returning something else sensible ("answer"/"don't answer"). Floor of 1 for a reasonable attempt.

  1. (6 points) Write a function that takes a tuple and returns a version that is rotated by a specified integer amount (positive=right, negative=left), i.e. def rot(t,r): For example, rot((1,2,3),2) or rot((1,2,3),-1) should both result in (2,3,1). The tuple can be any length: e.g. rot((7,5,4,9,8),1) should produce (8,7,5,4,9). You can assume that the absolute value of r is less than len(t). rubric: -1 per silly error (failure to return a value, etc.); -2 per logical error. This might be the hardest one to correct. answer:

    def rot(t,r):
    return(t[-r:]+t[:-r])
  2. (4 points) What is the outcome of the following Python code?

L1 = [[0,0],[0,0]]
L2 = L1
L2[0][0] = 2
print(L1)

Give a short (single sentence/phrase) explanation of what’s going on here. answer: the answer is [[2,0],[0,0]]. Because lists are mutable and we didn’t do anything fancy to get a deep copy, L2 and L1 point to the same memory location and changing the contents of L2 also changes the value of L1. rubric: 2 points for the right answer; 2 points for the explanation (anything reasonable mentioning “mutability”, “shallow copy”, “pointing to the same memory” should count)

  1. (4 points) What is the outcome of the following Python code? Explain what’s going on.
z = 1
def fun(z):
    z = z + 1
    print(z)
    return(z)

print(fun(z))
print(z)

answer: prints 2, 2, 1. Since numbers are immutable, changing the copy inside the function leaves the original value unchanged. rubric: 2 answer/2 explanation; explanation should mention something about immutability and function scope.

  1. (3 points for each separate example) Suppose we are trying to write a function that will approximate the expression \(\sum_{k=0}^\infty x^{-k}\) by adding terms until the next term is less than a specified tolerance (we’ll assume \(x>1\)). Each of the following functions has a problem. Explain the problem and explain the behaviour of the function (error, infinite loop, wrong answer [be specific], etc.) rubric: 2 points for identifying the behaviour, 1 point for the explanation
def fun(x):
    newterm = 1
    k = 0
    v = 0
    while newterm>1e-5:
        newterm = x**(-k)
        v += newterm
    return(v)

answer: we forgot to modify k inside the while code block, so the function will loop forever (unless x is 0, but that’s an unnecessary subtlety).

def fun(x):
    newterm = 0
    k = 0
    v = 0
    while newterm>1e-5:
        newterm = x**(-k)
        v += newterm
        k += 1
    return(v)

answer: we initialized newterm to 0, so the while condition will be False the first time through, so the function will always skip the loop and return 0.

def fun(x):
    newterm = 1
    k = 0
    v = 0
    while newterm>1e-5:
        newterm = x**(-k)
        k += 1
    v += newterm
    return(v)

answer: we incremented v outside the body of the loop, so the function will return a value of v that is just the last term of the sum.

  1. (8 points) Write a function fun(val,div,target,maxit) that returns the number of times (up to a maximum of maxit) you need to successively divide val by div before it is less than target. (You can assume that val, div, and target are all positive numeric values, that div is >1 [although it shouldn’t really matter] and that maxit is a positive integer.) If the function reaches maxit and the value is still greater than target, it should raise a ValueError. rubric: also hard to grade. -1 for silly problems (bad definition, no return); -2 for logic problems, including forgetting the maxit check or mis-implementing it. There are several possibilities for how to test the maxit criterion.
def fun(val,div,target,maxit):
    """silly function for an exam"""
    it = 0
    while val>target and it<=maxit:
        val /= div ## or val = val/div
        it += 1    ## or it = it+1
    if it==maxit and val>target:
        raise ValueError
    return(it)