Let's write some test cases and practice some test-driven development:
tests = [[], # Desired answer: []
[1], # [1]
[1,2], # [1, 3]
[1,2,3], # [1, 3, 6]
[1,2,1,3]] # [1, 3, 4, 7]
for t in tests:
print(rec_cumsum(t))
If we add this to your code and run it, we get:
last=new_list[-1]
IndexError: list index out of range
Why is this? Apparently -1 is an out-of-range index. Why wouldn't new_list have a -1 index?
Aha. That happens if new_list is empty. So we need to address the base case first. While we're at it, let's also use @MartijnPieters' suggestion:
if len(numbers) <= 1:
return numbers
to obtain
def rec_cumsum(numbers):
''' Input: numbers - a list of numbers,
Output: a list of cumulative sums of the numbers'''
if len(numbers) <= 1:
return numbers
new_list=numbers
last=new_list[-1]
new_list.remove(last)
rec = rec_cumsum(new_list)
new_list.append(rec[-1]+last)
return last+rec
Now run the test again. This time we get
return last+rec
TypeError: unsupported operand type(s) for +: 'int' and 'list'
So now Python is saying last is an int and rec is a list, and we can not add the two together.
Okay, rec should be a list since it is the return value of rec_cumsum(new_list). What should replace last+rec?
Let's think in terms of a concrete example. If rec is [a, a+b] then we want to return [a, a+b, a+b+c]. How do we form a+b+c?
How about adding the last element in rec with the last element of numbers:
rec[-1]+last
We want to append this to the end of rec:
rec.append(rec[-1]+last)
Let's make that change and see what happens. But while we're editing, let's also clean up some code we never use. We can delete new_list.append(rec[-1]+last):
def rec_cumsum(numbers):
''' Input: numbers - a list of numbers,
Output: a list of cumulative sums of the numbers'''
if len(numbers) <= 1:
return numbers
new_list=numbers
last=new_list[-1]
new_list.remove(last)
rec = rec_cumsum(new_list)
rec.append(rec[-1]+last)
return rec
Now our program returns
[]
[1]
[1, 3]
[1, 3, 6]
[2, 3, 4, 7]
Hurray, our program runs without errors. But wait... it returns the wrong results. Look at the last line.
rec_cumsum([1,2,1,3]) is returning [2,3,4,7] whereas the correct answer is [1,3,4,7]. Why is the first number wrong?
It has to do with new_list.remove(last). This command removes the first occurrence of last from new_list. We want to remove the last occurrence.
So instead, let's use
new_list = numbers[:-1]
So the program becomes:
def rec_cumsum(numbers):
''' Input: numbers - a list of numbers,
Output: a list of cumulative sums of the numbers'''
if len(numbers) <= 1:
return numbers
new_list=numbers[:-1]
last=numbers[-1]
rec = rec_cumsum(new_list)
rec.append(rec[-1]+last)
return rec
Run the tests. It works! Now it is a good practice to look back at our solution and see how it can be tightened up and made more elegant.
I see we used new_list and last as temporary variables. Do we really need them?
def rec_cumsum(numbers):
if len(numbers)<=1:
return numbers
result = rec_cumsum(numbers[:-1])
result.append(result[-1]+numbers[-1])
return result