1

NOTE: corrected question!

It is well-known that itertools allows for easy creation of nested for loops using itertools.product. But the following is what I want and can't do yet. Using

lfl = int(input( "length of first loop: "))
nol = int(input( "number of loops: "))

Causing:

  • length of loop: 12
  • number of loops: 4

I want an equivalent to:

for i1 in range(1,12):
    for i2 in range(i1,12):
        for i3 in range(i2,12):
            for i4 in range(i3,12):
                function(i1,i2,i3,i4)

itertools.product does way to many.

Or the more general question where nol causes the creating of func_1(x), func_2(x,y), .... func_nol-1(x,y,...) and the code needs to be equivalent to:

for i1 in range(1,12):
    for i2 in range(func_1(i1),12):
        for i3 in range(func_2(i1,i2),12):
            for i4 in range(func_3(i1,i2,i3),12):
                function(i1,i2,i3,i4)

And one more further generalization would be

for i1 in range(1,12):
    for i2 in range(start_func_1(i1, *global),end_func_(12, *global)):
        for i3 in range(start_func_2(i1,i2,*global),end_func_2(12,*global):
            for i4 in range(start_func_3  etc....
  • What you mean by *product does way to much.*? – Mazdak Apr 12 '15 at 19:33
  • @timothy How were you using `product`? – matsjoyce Apr 12 '15 at 19:42
  • @Kasra I mistyped by inner loop ranges, I have edited the question. – timothy sorenson Apr 15 '15 at 20:16
  • @matsjoyce, My apologies, one way using `product` was to use an upper bound range for every one of the loops, so as an example from the question above, my comment about 'way to much' was that `product` over [1,12] four times includes the results I am seeking. But many others I don't want. – timothy sorenson Apr 15 '15 at 20:20
  • Are you sure you want `range(1, 12)`? You call `lfl = 12` the *length of first loop*, but that range has length 11. – Lynn Apr 15 '15 at 20:24
  • @MaurisVanHauwe the subtlies of 0-11, 0-12, or 1-11 or 1-12 are not what I am worried about and would be correct for in testing! The big question is an indeterminate number of loops and the dependence of inner loop range on outer loop values. – timothy sorenson Apr 15 '15 at 21:00

3 Answers3

1

For your corrected question, try combinations_with_replacement instead of product on your list of ranges:

from itertools import combinations_with_replacement

nums = [10, 11, 12, 13]
for c in combinations_with_replacement(nums, 3):
    print c

prints

(10, 10, 10)
(10, 10, 11)
(10, 10, 12)
(10, 10, 13)
(10, 11, 11)
(10, 11, 12)
(10, 11, 13)
(10, 12, 12) 
...
Lynn
  • 10,425
  • 43
  • 75
  • Unfortunately, that `tool` returns `(n+r-1)! / r! / (n-1)!` different tuples. So in my corrected example, that would overshoot again substantially. Would really like tuples meeting exactly the criteria without a test if it has the correct conditions ie (i4>=i3>=i2>=i1) – timothy sorenson Apr 15 '15 at 20:58
  • Well, I don't see what this is lacking. You want `i1 ≤ i2 ≤ i3 ≤ i4`, right? This code generates exactly the sequences for which that holds if you pass it, say, `range(1, 12)` and `4`. – Lynn Apr 15 '15 at 21:10
  • Your comment was dead on for the criteria I gave in the first example, my mind had already moved to the general question. But your answer is beautiful and is correct. Unfortunately, a minor variation to say i4>i3>i2>1 immediately causes a problem. SO combination_with_replacement is exactly equivalent to my first question. However, I should have asked my general question right off the bat because that is what I am really interested in! I Must remember to be perfectly precise! – timothy sorenson Apr 15 '15 at 21:50
  • If `i4 > i3 > i2 > i1` is what you really need, try `itertools.combinations`. Otherwise, a function for this using a list of functions is going to be an unreadable mess; are you sure you need that much generalization? – Lynn Apr 15 '15 at 23:57
  • actually yes. The simplest case of the question I am looking at is solved with your solution and right now I have a nice program running that finds arbitray sets of integers, on user specified intervals, that produce certain statistically properties, say integer value mean and standard deviation. Another is fiinding bounds on roots of nth-derivatives using certain complex valued functions. These bounds, lower and upper, vary substantially based on the other roots. For large polynomials the iterated loops can become quite numerous and the integer ranges get quite large. – timothy sorenson Apr 18 '15 at 03:52
0

You can do this using product - you just need to create the 'dimensions' of the loops in an initial preparation step:

from itertools import product

length = 12
loops = 4

ranges = [range(x+1,length) for x in range(loops)]

def f(x):  # accepts a tuple, since the number of arguments may vary
    print("f(%s)" % repr(x))

for t in product(*ranges):
    f(t)
DNA
  • 42,007
  • 12
  • 107
  • 146
0

This is exactly equivalent to the code you provided:

from itertools import product

lfl = 12
nol = 4

ranges = (range(i, lfl) for i in range(1, nol + 1))
args = product(*ranges)

for arg in args:
    function(*arg)

It uses a generator expression to build the list of ranges used for the loops to pass to itertools.product and argument list unpacking to pass a single tuple of arguments as separate arguments to the function / to product().

Demonstration:

from itertools import product

def function(*args):
    return args

results_nested = []
results_product = []

for i1 in range(1, 12):
    for i2 in range(2, 12):
        for i3 in range(3, 12):
            for i4 in range(4, 12):
                results_nested.append(function(i1, i2, i3, i4))

lfl = 12
nol = 4

ranges = (range(i, lfl) for i in range(1, nol + 1))
args = product(*ranges)

for arg in args:
    results_product.append(function(*arg))


assert results_nested == results_product
Community
  • 1
  • 1
Lukas Graf
  • 30,317
  • 8
  • 77
  • 92
  • I am very sorry but I had an error in my question. Actually typos. I appreciate the answers especially the discover of *args that I did not know about. I am going to re ask the question in an answer form. – timothy sorenson Apr 15 '15 at 20:00
  • @timothysorenson no problem, it happens ;-) Thanks for letting me know and updating the question. – Lukas Graf Apr 16 '15 at 18:47