【问题标题】:Build a list using specific keys in a dict (python)?使用字典(python)中的特定键构建列表?
【发布时间】:2011-12-06 04:48:04
【问题描述】:

我正在 Python 中实现 Dijkstra 搜索算法。在搜索结束时,我使用前驱图重建最短路径,从目标节点的前驱开始。例如:

path = []
path.append(destination)
previous = predecessor_map[destination]
while previous != origin:
    path.append(previous)
    previous = predecessor_map[previous]

有没有什么办法可以用更少的代码行(例如列表理解)?

【问题讨论】:

  • 这很清楚。不要试图让它成为一个班轮来破坏它。我想你可以创建一个迭代器来产生每个前任并对其使用列表推导,但这只是隐藏了逻辑。
  • 你真的没有一个列表可以在理解中迭代,所以我猜没有。您的代码很简单,您可能完成的任何事情都将是纯代码高尔夫。

标签: python list search dictionary dijkstra


【解决方案1】:

我唯一的建议是摆脱轻微的代码重复:

path = []
previous = destination
while previous != origin:
    path.append(previous)
    previous = predecessor_map[previous]

除此之外,我认为您的代码实际上非常清晰,不太可能从任何缩短它的尝试中受益。

最后,值得注意的是,上述方法在 destination == origin 时也有效,而您的原始版本很可能不适用(取决于 predecessor_map 的填充方式)。不知道这是否与您的用例有关。

【讨论】:

  • @SvenMarnach:谢谢。我已经更新了我的答案以反映这一点。
  • 这和原来的代码不同,注意原来返回的列表有至少一个元素,destination
  • @ÓscarLópez:见我回答的最后一段。
  • @ÓscarLópez:重新考虑你的反对意见。在origin == destination 的情况下,原始代码将尝试查找原点的前身,这可能会引发KeyError。与原始代码的差异不是错误,而是错误修复!
【解决方案2】:

这可能有效:

path = [destination]
path += iter(lambda: predecessor_map[path[-1]], origin)

它的行为与您的原始代码相同。但是你已经写的就可以了。

如果destination 可以等于origin

path = []
path += iter(lambda: predecessor_map[path[-1]] if path else destination, origin)

它的行为与@aix's code 相同。

【讨论】:

  • 很好,只是你不能将+=iter() 转换为list。但是如果你使用path.extend(),它应该可以工作。
  • @glglgl 您可以将+=iter() 转换为list。该代码有效。
  • 糟糕,我不知道这一点。我想到了[] + () 的行为——非常不一致……
【解决方案3】:
def backwalk(mymap, start, origin):
    yield start
    current = mymap[start]
    while current != origin:
        yield current
        current = mymap[current]

path = list(backwalk(predecessor_map, destination, origin))

这将步行和收集任务分开。

如果你能确保你从不从原点开始,你可以简化为

def backwalk(mymap, start, origin):
    current = start
    while current != origin:
        yield current
        current = mymap[current]

【讨论】:

  • 如果我正在阅读代码,我会比这个更快地理解原始版本。
  • 不是特别短,但对于假设的可重用图形类中的客户端来说,迭代器将是一个更好的 API。
【解决方案4】:

假设predecessor_map 是一个dict 映射节点到父节点并且None 是根节点,您可以递归遍历边:

predecessor_map={0: None, 1: None, 2: 1, 3: 1, 4: 0, 5: 1}

定义一个反向遍历树的递归函数:

def path(node, predecessors):
    return [None] if node is None else [node] + path(predecessors.get(node), predecessors)

或者,如果你敢的话,一个 Y 组合子:

Y=lambda f: (lambda x: f(lambda *args: x(x)(*args)))(lambda x: f(lambda *args: x(x)(*args)))
path=Y(lambda f: lambda node, p: [None] if node is None else [node] + f(p.get(node), p))

使用中(使用列表理解):

>>> print [node for node in path(None, predecessor_map)]
[None]
>>> print [node for node in path(0, predecessor_map)]
[0, None]
>>> print [node for node in path(1, predecessor_map)]
[1, None]
>>> print [node for node in path(2, predecessor_map)]
[2, 1, None]
>>> print [node for node in path(3, predecessor_map)]
[3, 1, None]
>>> print [node for node in path(4, predecessor_map)]
[4, 0, None]
>>> print [node for node in path(5, predecessor_map)]
[5, 1, None]

【讨论】:

    【解决方案5】:

    另一种可能的解决方案是使用带有延迟输出的函数式编程:

    from itertools import tee, chain, imap, takewhile
    
    predecessor_map = {2:1, 3:2}
    destination = 3
    origin = 1
    
    def backwalk(predecessor_map, start, origin):
    
        def deffered_output():
            for i in output:
                yield i
    
        result, a = tee(deffered_output())
        b = imap(predecessor_map.get,a)
        output = takewhile(lambda x: x!=origin,chain([start],b))
    
        return result
    
    print(list(backwalk(predecessor_map,destination,origin)))
    

    我个人不会使用这种方法。不过这对训练来说很有趣。

    说明 关键元素是deferred_output,它将调用推迟到output。 然后我们使用teeoutput 拆分为2 个迭代器。 然后我们将predecessor_map.get 应用于名为a 的第二个迭代器,并将新的迭代器分配给b。 然后我们用takewhile控制输出,当达到origin时停止。

    【讨论】:

      【解决方案6】:

      我认为您无法通过理解来完成此迭代。也许你可以把它简化一点,像这样:

          path, previous = [], destination
          while True:
              path.append(previous)
              previous = predecessor_map[previous]
              if previous == origin:
                  break
      

      上面的循环使用 do..while 会更好看,但 Python 缺少它

      【讨论】:

        猜你喜欢
        • 2022-11-12
        • 1970-01-01
        • 2021-02-08
        • 2022-11-01
        • 1970-01-01
        • 2015-06-30
        • 2015-09-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多