【问题标题】:Python 3.4 nested loop using lambda filter working weirdly使用 lambda 过滤器的 Python 3.4 嵌套循环奇怪地工作
【发布时间】:2015-04-27 19:13:50
【问题描述】:

我正在尝试使用 NLTK 的文本拼接代码 (https://github.com/nltk/nltk/blob/develop/nltk/tokenize/texttiling.py)。

这是一个根据内容将文档输入分割成几个图块的代码。我注意到通过将整个文本作为一个平铺返回,某些文档的平铺根本不起作用,并且发现这部分代码的工作方式很奇怪。

    depth_tuples = sorted(zip(depth_scores, range(len(depth_scores))))
    depth_tuples.reverse()
    hp = filter(lambda x:x[0]>cutoff, depth_tuples)

    for dt in hp:
        boundaries[dt[1]] = 1
        for dt2 in hp: #undo if there is a boundary close already
            if dt[1] != dt2[1] and abs(dt2[1]-dt[1]) < 4 \
                   and boundaries[dt2[1]] == 1:
                boundaries[dt[1]] = 0
    return boundaries

Depth_tuple 是一个包含元组列表 [(score, index)] 的列表,而 hp 是一个过滤结果,其分数大于某个截止值。

使用嵌套循环,它对 hp 的每个条目分别迭代两次。换句话说,对于 hp 的每个条目,它应该检查 hp 的所有条目。但我注意到第二个循环(对于 hp 中的 dt2)在第一次迭代后没有执行。这就像 dt2 指针到达第一个 dt 的 hp 末尾,并且它没有为新的迭代初始化。

给你一个这个现象的简化例子, 说 x = [(0.6,3),(0.2,1),(0.5,2),(0.4,3)]

如果截止值为 0.3,则 hp 包含 [(0.6,3), (0.5, 2), (0.4, 3)]

所以循环应该是这样的

当 x = (0.6, 3) 时,第二个循环检查 [(0.6,3), (0.5, 2), (0.4, 3)]

当 x = (0.5, 2) 时,第二个循环再次检查 [(0.6,3), (0.5, 2), (0.4, 3)]

但它只在 x=(0.6, 3) 时这样做,并且对于 x 的其余部分,第二个循环不会运行。

我最初怀疑迭代器在第二个循环中已经到达了 hp 的末尾,但它无法解释第一个循环的 hp 中的迭代器如何仍然可以去......

您能解释一下为什么会这样吗?谢谢!

【问题讨论】:

  • 附注:首先,sorted 采用 reverse=True 关键字参数,因此您无需在单独的步骤中进行排序,然后反转。接下来,如果您的数据按您的键排序,使用takewhile 比使用filter 更有意义。毕竟,一旦第一个失败,其余的都会失败,所以没有理由继续检查它们。
  • 同时,请尝试将其重组为minimal, complete, verifiable example,其中包括演示问题的示例输入(例如,硬编码到变量中,如depth_tuple = [(1, 4), (7, 3), (3, 2)])。
  • 还有,这是哪个 Python 版本?
  • 最后,当你说“第一次迭代后没有执行第二次循环”时,这是否意味着你进入了外循环的第二次迭代(并设置boundaries[dt] = 1),但内循环是空的?还是第一个循环也结束了?
  • @abarnert 更新了示例并添加了 python 版本!我的意思是内部循环是空的。

标签: python loops python-3.x lambda


【解决方案1】:

您正在使用 Python 3,并且配方是为 Python 2 编写的。在 Python 2 中,filter 返回一个 list,显然可以使用 for 多次迭代(内部 for dt2 in hp )。

但是在 Python 3 中,hp 将是 a one-pass iterator;现在,外部for 将消耗第一个元素,而内部for 将消耗所有剩余元素;当内循环退出时,外循环找到一个空的迭代器也退出。

或者,正如 Python 2 和 3 文档所说,在 Python 2 中 filter(function, iterable) 相当于列表推导

[item for item in iterable if function(item)]

在 Python 3 中,它相当于生成器表达式

(item for item in iterable if function(item))

作为最简单的解决方法,将filter 返回的迭代器变成list

hp = list(filter(lambda x: x[0] > cutoff, depth_tuples))

【讨论】:

    【解决方案2】:

    我不知道为什么 Dan D. 删除了his answer。也许它没有完全解释问题,但它确实提供了正确的解决方案和您所缺少的关键信息。

    假设这是 Python 3,filter 返回一个 iterator,而不是一个序列。迭代器只能迭代一次。迭代器知道它的“当前位置”,并在您请求时懒惰地产生值;一旦你要求了所有的价值,就没有更多的价值可以给出。所以,例如:

    >>> hp = iter([1,2,3])
    >>> for dt in hp:
    ...     print(dt)
    1
    2
    3
    >>> for dt in hp:
    ...     print(dt)
    

    第二次,它什么也不打印,因为你已经使用了所有的值。

    同样的事情发生在嵌套循环中:

    >>> for dt in hp:
    ...     print(dt)
    ...     for dt in hp:
    ...         print('>', dt)
    1
    > 2
    > 3
    

    在外循环的第一次迭代中,dt 获得第一个值。然后嵌套的内循环获取所有其余的值,这样外循环就完成了。

    如果你想反复迭代某些东西,最简单的做法就是将其转换为序列:

    hp = list(hp)
    

    在某些情况下,使用tee 可能更有效和/或更简洁,但这不适用于此处。您的代码旨在将hp 视为一个序列,因此只需将其设为一个序列即可。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-24
      • 1970-01-01
      • 1970-01-01
      • 2021-01-17
      • 1970-01-01
      • 1970-01-01
      • 2017-05-09
      相关资源
      最近更新 更多