简体   繁体   中英

Python: remove empty lists from within comprehension

I am implementing dijkstras algorithm to compute shortest path. My question is just if there is a cleaner way to implement the following comprehension (ie without if [b for a,b in G[x] if a not in X]!=[]] tacked onto the end).

In the following, G is a graph where its keys are the graphs nodes, and each node has a list of tuples representing its connecting edges. So each tuple contains the information: (connected node, distance to connected node). X is a set of nodes that the algorithm has already looked at, and A is dictionary mapping those nodes that have already been found to the shortest distance to them from the starting node, in this case node 1.

UPDATE: sorry i gave an example that worked, here is one that does not work if the last part of the comprehension is removed.

G = {1: [(2, 20), (3, 50)], 2: [(3, 10), (1, 32)], 3: [(2, 30), (4, 10)], 4: [(1, 60)]}
X = {1,2,3}
A = {1: 0, 2: 20, 3:30}

mindist = min([A[x] + min([b for a,b in G[x] if a not in X]) for x in X if [b for a,b in G[x] if a not in X]!=[]])

The question is how to write mindist as a comprehension that can deal with taking min([[],[some number],[]) .

The last part, if [b for a,b in G[x] if a not in X]!=[]] just removes the empty lists so min doesnt fail, but is there a better way to write that comprehension so there are no empty lists.

Here's an idea:

minval = [float('+inf')]
min(A[x] + min([b for a, b in G[x] if a not in X] + minval) for x in X)
=> 40

The trick? making sure that the innermost min() always has a value to work with, even if it's a dummy: a positive infinite, because anything will be smaller than it. In this way, the outermost min() will ignore the inf values (corresponding to empty lists) when computing the minimum.

First of all, the empty list is considered false in a boolean, so you don't need to test for inequality against [] ; if [b for a,b in G[x] if a not in X] is enough.

What you really want to do is to produce the inner list once , then both test and calculate the minimum in one go. Do this with an extra inner 'loop':

mindist = min(A[x] + min(inner) 
              for x in X 
              for inner in ([b for a,b in G[x] if a not in X],) if inner)

The for inner over (...,) loop iterates over a one-element tuple that produces the list once , so that you can then test if it is empty ( if inner ) before then calculating the A[x] + min(inner) result to be passed to the outer min() call.

Note that You do not need a list comprehension for that outer loop; this is a generator expression instead, saving you building up a list object that is then discarded again.

Demo:

>>> G = {1: [(2, 20), (3, 50)], 2: [(3, 10), (1, 32)], 3: [(2, 30), (4, 10)], 4: [(1, 60)]}
>>> X = {1,2,3}
>>> A = {1: 0, 2: 20, 3:30}
>>> min(A[x] + min(inner) 
...               for x in X 
...               for inner in ([b for a,b in G[x] if a not in X],) if inner)
40

Ok, I had to unpack that nutty list comprehension to figure out what you were talking about - I think this is approximately the same code:

dists = []
for x in X:
    newdists = [b for a,b in G[x] if a not in X]
    if len(newdists) > 0
        dists.append(A[x] + min(newdists))
mindist = min(dists)

Here's an approach that eliminates the test in question:

import sys

def newMin(values):
    if len(values) == 0:
        return float('inf')
    else:
        return min(values)

mindist = min(A[x] + newMin([b for a,b in G[x] if a not in X]) for x in X)

print mindist

output:

40

It's not a full solution, but since you're just getting rid of empty lists, you could shorten the condition. An empty list is evaluated as False. A list with anything in it is evaluated as true. Just provide the list as the condition. This is true for all empty built-in data types.

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