This is the iteration of a nested loop

Asked

Viewed 62 times

1

How to use generators/iterators to achieve the generic iteration of a nested loop? I have the following function implemented in an extremely inefficient way.

from pprint import pprint

def center(i, j):
    s = 50
    for k, v1 in enumerate(range(int(s / 2), s * 8, s)):
        for n, v2 in enumerate(range(int(s / 2), s * 8, s)):
            if (i, j) == (k, n):
                return (v1, v2)

pprint(center(0,0))  # -> (25, 25)
pprint(center(6,3))  # -> (325, 175)

I tried to implement a generator to get only c(x, y) but don’t know how to do it.

def centerGenerator():
    s = 50
    for k in range(int(s / 2), s * 8, s):
        for n in range(int(s / 2), s * 8, s):
            yield k, n

def center(i, j):
    for c in centerGenerator():
        pass # como obter c(i, j) do gerador?

pprint(center(0,0))  # -> (25, 25)
pprint(center(6,3))  # -> (325, 175)

I would like to get only the desired iteration without having to generate from the beginning to the necessary point. As if it were a mathematical function, but obviously, using generators/iterators to be more efficient.

1 answer

5


I would like to get only the desired iteration without having to generate from start to the necessary point

If you need this, then you don’t need a loop, or generators, or anything - just a coordinate transformation for your coordinate system - which, given a scale s is to basically sum an offset of s/2, and increases s in s:

def center(i, j, s=50):
   return s // 2 + i * s, s // 2 + j * s

And, unless the generator relies on either a side effect done at each step (for example, reading one or more lines from a file), or a cumulative effect at each step (for example, going through a vector and adding up the accumulated values), you will always be able to go straight to the desired point, just doing the math.

For cases where previous steps are needed, then steps are needed - the function itertools.islice can provide a shorter way to traverse all the required points of a generator before the desired point - but it doesn’t make it any easier to understand who is looking.

For example, for the generator:

def acumulador(n):
    valor = 0
    for i in range(0, n):
        valor += i
        yield valor

If I want to print only the values between 6 and 8 of the iteration I can do:

from itertools import islice

for i in islice(acumulador(10), 6, 8):
    print(i)

But note that the islice there is no way to "guess" what the iterator does at each step - the only thing it does is select the values requested in the call, but internally all the values of the accumulator are used, until you arrive at the seventh interaction (which has index 6) - there, instead of discarding the value, he does yield of it, and the value is used in for external.

Reiterating then: to be able to go straight to a value of the loop has nothing to do with the possibilities of the programming language, but rather with the simple logic: or the value Depends on the previous results to be calculated, and if it depends, you have to go through all the interactions, you have nothing to do (the language even has the islice precisely to "hide" the interactions that are not necessary), or, as in the example of the question, the value is independent of the calculations of the previous values in the interaction, and only depends on the numerical variables: in this case, just do this, the direct account without any tie.

  • I get it. The mathematical solution is always more efficient. So, just to put an end to this, in the case that I have two nested loops use the islice to get the values of example (center(0.0) and center(6.3)) would it look like? In the case as would the arguments for the call.

  • The "islice" would not be appropriate in this case (see that its center function does nothing, the islice would be direct to the centerGenerator - but you want to find a solution that depends on two parameters, and the islice simply call the generator sequentially, until you reach the desired position.

  • So, if we were to insist on doing this, we can, knowing that in this particular case, the width of each line is "8" - then we transform i and j into the linear position before calling the islice, and use the call next to recover the first value released by the islice: next(islice(centerGenerator(), i * 8 + j, i * 8 + j + 1))

  • Actually, this one looks more like the use case for a class than for generators - you create a class, which has as parameters the scale and possibly the size of your grid (in case 8 x 8), and put methods and properties to convert the coordinates into pixels (I suppose) for the internal coordinates, and iterate all pixel coordinates if applicable.

  • Right. The biggest interest is to know if it is possible to obtain only the umpteenth value of the iteration of a nested loop or loop. Without having to generate all the values until that iteration. I think I read somewhere about generators and iterators and I got that possibility in my mind. I just picked up a comparison of an old checkers script to try to implement this.

  • (I passed this reply to the comment to the last paragraph in the same reply)

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.