Even A. Rognlien
Even A. Rognlien

Reputation: 125

Understanding Python's itertools.chain and next

I'm trying to convert a line of Python code to JavaScript, but I have limited Python knowledge and struggle to make sense of it.

Can anyone please try to explain the following line of code? The point_orientation function is not important, it just returns True/False.

i_extend = next( ( i for i in itertools.chain(range(i_start+1, len(p)), range(0,i_start+1)) if not point_orientation( p[i-1], p[i], p[(i+1) % len(p)] ) ) )

Upvotes: 0

Views: 1884

Answers (2)

chepner
chepner

Reputation: 532093

chain stitches two iterators together into one. Here, it's being used to help simulate a closed cycle starting at a particular point. Observe that

range(0, len(p)) == chain(range(0, i_start+1), range(i_start+1, len(p))

The given code swaps the arguments to chain, so that it converts a sequence like [0,1,2,...,10] into a sequence like [5,6,...,10,0,1,...,4]. The three arguments to the predicate are just adjacent items in the cycle, with (i+1) % len(p) doing the wrapping around at the end of the sequence. next is just used to get the first element of the resulting sequence.

If you gave up on trying to express this in a one-liner, you might write the more understandable code that follows:

n = len(p)
for i in range(i_start + 1, i_start + 1 + n):
    index_before = (i - 1) % n
    index_at = i % n
    index_after = (i+1) % n
    if not point_orientation(p[index_before], p[index_at], p[index_after])
        i_extend = y
        break

We iterate over a single range, and do modular arithmetic on the each index inside the loop. If the predicate passes, we set i_extend to the current point and break out. Otherwise, we continue the iteration, under the assumption that one of the points will eventually succeed.

Upvotes: 1

Alex Hall
Alex Hall

Reputation: 36043

It means "find the first element i in range(i_start+1, len(p)) or range(0,i_start+1) (if the first range doesn't have one) such that point_orientation( p[i-1], p[i], p[(i+1) % len(p)] ) is false". If there is no such i, it will raise an exception.

Here it is in more verbose Python:

def check(i):
    return point_orientation(p[i - 1],
                             p[i],
                             p[(i + 1) % len(p)])


def find_i_extend():
    for i in range(i_start + 1, len(p)):
        if not check(i):
            return i

    for i in range(0, i_start + 1):
        if not check(i):
            return i


i_extend = find_i_extend()

Upvotes: 2

Related Questions