简体   繁体   中英

python: recursive expanding list comprehension

I've been looking around and I don't quite find my specific question answered. What makes my question specific is that I'm trying to only use list comprehension.

Question:

With the following code:

[print(k,l,m) for k in range(3) for l in range(2) for m in range(1)]

I get the output:

0 0 0
0 1 0
1 0 0
1 1 0
2 0 0
2 1 0

This works for n=3, or any hardcoded n. However, what I would like to do is:

[print(k1,k2,...,kn) for k1 in range(n) for k2 in range(n-1) .. for kn in range(1)].

I hope someone out there sees the answer immediately-- and thanks!

Edit The solution was posted below, but also to be clear about the format I was hoping for, here's how I used it to print out the encoding tuples:

n=3
[print(x) for x in itertools.product(*[list(range(i)) for i in range(n,0,-1)])]

(Didn't use reverse on the range for simplicity).

You could use itertools.product if you are allowed to :

n=3
list(itertools.product(*[list(range(i)) for i in reversed(range(1, n+1))]))

Someone recently asked a very similar question: "[How to] determine all combinations of flipping a coin without using itertools.product ?" to which I provided an answer for. I wouldn't consider it a duplicate because I believe itertools.product is the best fit for your particular problem.

You may however be wondering how itertools.product does the trick. Or maybe you want to encode a different transformation in your recursive expansion. Below, I'll share one possible solution using Python's generators

def product (*iters):
  def loop (prod, first = [], *rest):
    if not rest:
      for x in first:
        yield prod + (x,)
    else:
      for x in first:
        yield from loop (prod + (x,), *rest)
  yield from loop ((), *iters)

for prod in product ("ab", "xyz"):
  print (prod)

# ('a', 'x')
# ('a', 'y')
# ('a', 'z')
# ('b', 'x')
# ('b', 'y')
# ('b', 'z')

Because product accepts aa list of iterables , any iterable input can be used in the product. They can even be mixed as demonstrated here

print (list (product (['@', '%'], range (2), "xy")))
# [ ('@', 0, 'x')
# , ('@', 0, 'y')
# , ('@', 1, 'x')
# , ('@', 1, 'y')
# , ('%', 0, 'x')
# , ('%', 0, 'y')
# , ('%', 1, 'x')
# , ('%', 1, 'y')
# ]

We could make a program foo that provides the output posted in your question

def foo (n):
  def build_ranges (m):
    if m == 0:
      return []
    else:
      return [ range (m) ] + build_ranges (m - 1)
  yield from product (*build_ranges (n))


for prod in foo (3):
  print (prod)

# (0, 0, 0)
# (0, 1, 0)
# (1, 0, 0)
# (1, 1, 0)
# (2, 0, 0)
# (2, 1, 0)

Or use destructuring assignment to create bindings for individual elements of the product

for (x,y,z) in foo (3):
  print ("x", x, "y", y, "z", z)

# x 0 y 0 z 0
# x 0 y 1 z 0
# x 1 y 0 z 0
# x 1 y 1 z 0
# x 2 y 0 z 0
# x 2 y 1 z 0

Other qualities of this product implementation are discussed in my answer to the question I linked. If you're interested to see how to do this without generators, I provide a purely functional solution to the problem as well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM