Recursion is a functional heritage and so using it with functional style yields the best results. This means avoiding things like mutation, variable reassignments, and other side effects.
We can write insertion_sort(t) using inductive reasoning. Starting with the input source src = t and an empty destination array dst = [] -
- If
src is empty, there is nothing left to sort. Return the dst array
- (inductive)
src has at least one element. insert the first element src[0] into dst and recur on the sub-problem src[1:]
def insertion_sort(t):
def run(src, dst):
if not src:
return dst # 1
else:
return run(src[1:], insert(dst, src[0])) # 2
return run(t, [])
insert(t, x) can be written using the same technique -
- If the input
t is empty, the only possible output is [x]
- (inductive)
t has at least one element. If the first element t[0] is less than the element to insert x, then prepend t[0] to the result of the recursive sub-problem insert(t[1:], x)
- (inductive)
t the first element is greater than or equal to x. Prepend x to t
def insert(t, x):
if not t:
return [x] # 1
elif t[0] < x:
return [t[0]] + insert(t[1:], x) # 2
else:
return [x] + t # 3
Finally print the result
arr = [10, 8, 7, 50, 60, 3, 9, -1]
print(insertion_sort(arr))
[-1, 3, 7, 8, 9, 10, 50, 60]
visualize
Writing out the evaluation steps can help aid our ability to visualize how recursive computations grow. First we look at the simpler of the two, insert(t,x)
insert([3, 7, 8, 10, 50, 60], 9)
== [3] + insert([7, 8, 10, 50, 60], 9)
== [3] + [7] + insert([8, 10, 50, 60], 9)
== [3] + [7] + [8] + insert([10, 50, 60], 9)
== [3] + [7] + [8] + [9] + [10, 50, 60]
== [3, 7] + [8] + [9] + [10, 50, 60]
== [3, 7, 8] + [9] + [10, 50, 60]
== [3, 7, 8, 9] + [10, 50, 60]
== [3, 7, 8, 9, 10, 50, 60]
And now let's visualize insertion_sort(t) -
insertion_sort([10, 8, 7, 50, 60, 3, 9, -1])
== run([10, 8, 7, 50, 60, 3, 9, -1], [])
== run([8, 7, 50, 60, 3, 9, -1], [10])
== run([7, 50, 60, 3, 9, -1], [8, 10])
== run([50, 60, 3, 9, -1], [7, 8, 10])
== run([60, 3, 9, -1], [7, 8, 10, 50])
== run([3, 9, -1], [7, 8, 10, 50, 60])
== run([9, -1], [3, 7, 8, 10, 50, 60])
== run([-1], [3, 7, 8, 9, 10, 50, 60])
== run([], [-1, 3, 7, 8, 9, 10, 50, 60])
== [-1, 3, 7, 8, 9, 10, 50, 60]
j = i - 1)