Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to iterate more pythonic for a case?

Tags:

python

Here is the case: mylist = range(100) and I have a discontinuous path from src=1 to dest=99 with an array pre[100], each value of which represents the last elem in the path. The question is: How can I get all the elems in the path from dest to src. Can do like this:

    path = []
    i = dst
    while i != src:
      path.append(i)
      i = pre[i]
    path.append(src)

But, is there any simpler way maybe using just only one statement?

Sample Input

dst, src = 3, 2
pre = [2, 0, 3, 1]

Output

[3, 1, 0, 2]   #It just have to follow the path with the indices from 3 to 2.

Explanation:

              src  dst
                v  v
Indices:  0  1  2  3
Pre    :  2  0  3  1

From dest 3, the predecessor is 1, so we go to 1.
From node 1, the predecessor is 0, so we go to 0.
From node 0, the predecessor is 2, so we go to 2.
2 is src, so we're done.
like image 383
user2994754 Avatar asked Dec 26 '22 17:12

user2994754


2 Answers

The Pythonic way is to either use your existing loop, or to use an equivalent generator function:

def gen_path(src, dst, pre):
    while dst != src:
        yield dst
        dst = pre[dst]
    yield src

Then to get a list, you'd call it with list(gen_path(src, dst, pre)).

There is no way to do this in a single basic expression, because there is state involved (what node in the path you're on). It probably is possible using complicated, hackish stuff like 2-argument next and lambdas with default arguments, but you don't want to go there.

like image 176
Blckknght Avatar answered Dec 28 '22 09:12

Blckknght


NOTE: I am not recommending this. In real code I would write the loop. The loop is simple and clear. The loop is already pythonic. Maybe use yield instead of materializing a list, but that's easy.

As I put it once: "What seems unpythonic to me is spending time worrying about making perfectly clear code more pythonic. The Pythonicness which needs hours of thought is not the true Pythonicness."


But for entertainment purposes, and because the Colts just won, a one-liner:

>>> from itertools import takewhile, accumulate, repeat
>>> dst, src = 3, 2
>>> pre = [2, 0, 3, 1]
>>> list(takewhile(lambda x: x != src, 
                   accumulate(repeat(dst), lambda x,y: pre[x]))) + [src]
[3, 1, 0, 2]

This will only work in Python >= 3.3, in which accumulate accepts a binary-argument function as its second argument. This comes in useful in cases where you want a reduce which gives the partial results, and reminding everyone about this new functionality is about the only excuse I have for this answer..

like image 22
DSM Avatar answered Dec 28 '22 08:12

DSM