Here's a recursive approach using a default argument. The numbered points below refer to the numbered comments in the code.
- (base) The depth is zero. We are done, so yield the combination,
comb
- (inductive) The depth is at least 1. For each
x in a range, delegate to the recursive generator using x as the new starting point for the nested range and append x to the combination, comb.
def nested_range (depth = 0, start = 0, end = 1, comb = ()):
if depth == 0:
yield comb #1
else:
for x in range(start, end): #2
yield from nested_range(depth - 1, x, end, comb + (x,))
Here's a nested range three (3) levels deep -
for p in nested_range (3, 0, 4):
print(p)
# (0, 0, 0)
# (0, 0, 1)
# (0, 0, 2)
# (0, 0, 3)
# (0, 1, 1)
# (0, 1, 2)
# (0, 1, 3)
# (0, 2, 2)
# (0, 2, 3)
# (0, 3, 3)
# (1, 1, 1)
# (1, 1, 2)
# (1, 1, 3)
# (1, 2, 2)
# (1, 2, 3)
# (1, 3, 3)
# (2, 2, 2)
# (2, 2, 3)
# (2, 3, 3)
# (3, 3, 3)
This implementation is a total function and provides a valid result when depth = 0 -
for p in nested_range (0, 0, 4):
print(p)
# ()
And for good measure, here's the output of a nested range five (5) levels deep -
for p in nested_range (5, 0, 3):
print(p)
# (0, 0, 0, 0, 0)
# (0, 0, 0, 0, 1)
# (0, 0, 0, 0, 2)
# (0, 0, 0, 1, 1)
# (0, 0, 0, 1, 2)
# (0, 0, 0, 2, 2)
# (0, 0, 1, 1, 1)
# (0, 0, 1, 1, 2)
# (0, 0, 1, 2, 2)
# (0, 0, 2, 2, 2)
# (0, 1, 1, 1, 1)
# (0, 1, 1, 1, 2)
# (0, 1, 1, 2, 2)
# (0, 1, 2, 2, 2)
# (0, 2, 2, 2, 2)
# (1, 1, 1, 1, 1)
# (1, 1, 1, 1, 2)
# (1, 1, 1, 2, 2)
# (1, 1, 2, 2, 2)
# (1, 2, 2, 2, 2)
# (2, 2, 2, 2, 2)
print(i, j, k)?