From the PacktPub's book "Python Data Structures and Algorithms" (authored by Benjamin Baka) section on Backtracking from chapter 3, they have this function (page 81):
def bitStr(n, s):
if n == 1: return s
return [digit + bits for digit in bitStr(1, s)
for bits in bitStr(n - 1, s)]
print(bitStr(3,'abc'))
Returns:
['aaa', 'aab', 'aac', 'aba', 'abb', 'abc', 'aca', 'acb', 'acc', 'baa', 'bab', 'bac', 'bba', 'bbb', 'bbc', 'bca', 'bcb', 'bcc', 'caa', 'cab', 'cac', 'cba', 'cbb', 'cbc', 'cca', 'ccb', 'ccc']
The book's explanation:
Notice the double list compression and the two recursive calls within this comprehension. This recursively concatenates each element of the initial sequence, returned when
n = 1
, with each element of the string generated in the previous recursive call. In this sense it is backtracking to uncover previously ingenerated combinations. The final string that is returned is all n letter combinations of the initial string.
Well, besides how unpythonic it feels by them using a list comprehension for something like this (or maybe it's just me), I simply can't understand it.
I rewrote the whole thing and added my comments to the code with what I understood so far and where I'm stuck.
def test(n, s):
# this one's obvious
if n == 1: return s
result = list() # so's this
"""
From what I can tell, the first recursive call in the outer
loop forces the function's conditional statement to
activate. So, with the example of `abc` as the second
input to the function, the outer for loop just loops
over that string, `abc` in this case.
"""
for digit in test(1, s):
"""
This is where I start to lose my grip on what's happening.
So, going to the above returned list as given by the book,
I can see where the first item, i.e `aaa`, in the list
comes from.
So, here's what I see when it comes to that first item.
`digit` now storing the first 'a' that later get's added,
the second recursive call in this second loop (if w-
e're going with the original example call from the
book: `test(3, 'abc')`) sets the first input to 2 on
the first iteration.
I'm guessing this makes it so it avoids the conditional
statement. With that avoided, we still have the string
'abc'.
The first iteration of the loop takes the first
letter, 'a', and adds it to `digit`.
I'm a bit confused on how then the loop goes back through
the function to get the third 'a' to complete the
final list's first item, 'aaa'.
Though, say I did understand that at the time of posting
this question, what I'm totally confused about it is
how this supposedly 'Backtracks', and gets the
final list's second item, 'aab', and all subsequent items.
"""
for bits in test(n-1, s):
result.append(digit + bits)
return result
Any help explaining this all would be appreciated. Plus, it would be great if someone can confirm correct what I "claim" to understand already in my code comments.