4.3. Homework Answers#

4.3.1. Question 1#

Solution to Exercise 3.1

a.

>>> val = 5.0
>>> increase_by_one(val)
>>> print(val)
6.0

This is not possible because the variable val is of type float which is an immutable type. The attempt below won’t work. Even though the local variable x and the global variable val reference the same object 5.0, the line x += 1 creates a new local variable x rather than mutating the value of the object 5.0.

def increase_by_one(x):
    x += 1

b.

>>> val = "hello"
>>> increase_by_one(val)
>>> print(val)
hello1

This is not possible for the same reason as a. Strings are immutable and so any attempt to change the value of the string will either fail,

def increase_by_one(x):
    x.append("")

…or result in the local variable x referencing a new object.

def increase_by_one(x):
    x += "1"

c.

>>> val = [0, 0, 0]
>>> increase_by_one(val)
>>> print(val)
[0, 0, 0, 1]

This is possible. The append method mutates the list. The local variable x and global variable val both reference the same list and mutating the list affects both variables.

def increase_by_one(x):
    x.append(1)

d.

>>> val = np.array([0, 0, 0])
>>> increase_by_one(val)
>>> print(val)
[0 0 0 1]

This is not possible. Even though the Numpy array is a mutable type, it is not possible to change the size of a Numpy array.

https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html

For example, the function numpy.append doesn’t mutate arrays, but returns a new one. This won’t work:

def increase_by_one(x):
    np.append(x, 1)

Marks
1 mark for identifying this case [c].
[a, b] 1 mark for stating that floats/strings are immutable and 1 mark for stating that the value referenced by immutable variables cannot be changed (or similar).
[c] 1 mark for a correct function definition. No marks if a return statment included, or if the function call has been changed (eg val = increase_by_one(val))
[d] Even though arrays are mutable 1 mark they are of fixed size (or do not have an append function or similar) 1 mark

4.3.2. Question 2#

One way to swap the two arrays is to make a temporary copy of the values. Perhaps the simplest way to do this is to copy all of the value into a temporary array first. This works:

def swap_lists(x, y):
    z = x[:] # create temporary copy of x
    x[:] = y # mutate value of x
    y[:] = z # mutate value of y

Note that this works because x[:] makes a copy of the values of x. Another way to do the exact same is x.copy().

This solution is fine, but we might like to consider if there are other solutions which don’t require making a copy of the list elements. For example, the following code moves one value at a time from x to y then one item at a time from y to x. Note that we must be careful to move items in the correct order - otherwise we end up revering the list!

def swap_lists(x, y):
    n = len(y)
    while len(x) > 0:
        item = x[0] 
        x.remove(item) # remove first element of x
        y.append(item) # append to end of y
    for i in range(n):
        item = y[0]
        y.remove(item) # remove first element of y
        x.append(item) # append to end of x  

In most applications this second method is probably not better (it is certainly more complicated) but it has one key advantage: it doesn’t copy any data and therefore doesn’t use any extra memory.

It is not possible to write a function which swaps two strings because strings are immutable (see Question 1d).

Marks

4 marks for a function which correctly swaps the arrays in place. 2 marks if the function works correctly except to reverse the order of the elements. Deduct 1 mark if a redundant return statement is included or an attempt is made to change global variables from inside the function, but the function otherwise works.

0 marks if the function does not swap the lists in-place or if the function call is changed to an assignment (eg x, y = swap_lists(x, y)).

1 mark for stating that strings are immutable.