brianpck - 1 year ago 61
Python Question

# Using list comprehension to create a list of lists in a recursive function

I am experimenting with recursive functions. My goal is to produce:

A function
`combo`
that generates all non-increasing series that add up to n

Some sample inputs/outputs:

``````>>> print (combo(3))
[[3], [2, 1], [1, 1, 1]]
>>> print (combo(4))
[[4], [3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]]
>>> print (combo(5))
[[5], [4, 1], [3, 2], [3, 1, 1], [2, 2, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1]]
``````

After a lot of trial and error, I came up with the following function which does exactly what I want:

``````def combo(n, limit=None):
if not limit:
limit = n
# return [ LIST COMPREHENSION HERE ]
res = []
for i in range(limit, 0, -1):
if i == n:
res.append([i])
else:
res.extend([i] + x for x in combo(n - i, min(n - i, i)))
return res
``````

My question: Is there a way (pythonic or not) to return this same result using a single list comprehension, without appending to and extending
`res`
?

Here are two things I've tried, along with their mangled results:

``````return [[i] if i == n else [[i] + x for x in combo(n - i, min(n - i, i))] for i in range(limit, 0, -1)]
# lists get deeper: [[4], [[3, 1]], [[2, 2], [2, [1, 1]]], [[1, [1, [1, 1]]]]]

return [[i if i == n else ([i] + x for x in combo(n - i, min(n - i, i))) for i in range(limit, 0, -1)]]
# parentheses interpreted as generator: [[4, <generator object combo.<locals>.<listcomp>.<genexpr> at 0x01D84E40>, etc.]]
``````

I realize that the answer may be quite ugly, but I've spent enough time trying that I just want to know if it is possible.

Convert extending to appending and it becomes easier to grok how to convert this:

``````res = []
for i in range(limit, 0, -1):
if i == n:
res.append([i])
else:
# this is the generator expression pulled out of the res.extend() call
for x in combo(n - i, min(n - i, i)):
res.append([i] + x)
``````

You can now move the `i == n` case into the other branch (using a helper `items` variable here for readability):

``````res = []
for i in range(limit, 0, -1):
items = [[]] if i == n else combo(n - i, min(n - i, i))
for x in items:
res.append([i] + x)
``````

If `i == n`, this causes the loop iterate once and produce an empty list, so you effectively get `res.append([i])`.

This can then trivially be converted to a list comprehension (inlining `items` into the `for` loop):

``````return [
[i] + x
for i in range(limit, 0, -1)
for x in ([[]] if i == n else combo(n - i, min(n - i, i)))]
``````

Wether or not you should is another matter; this is hardly easier to read.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download