【问题标题】:How can I get next element in for loop如何在 for 循环中获取下一个元素
【发布时间】:2012-11-01 22:47:09
【问题描述】:

我有一个包含很多 bool 值的列表,我想得到这个模式:True, True, False, True, True。为此,我认为我需要在处理上一个元素时循环获取下一个元素。我该怎么做?

额外问题:除了i += 1 之外,还有其他方法可以增加列表中元素的位置吗?

【问题讨论】:

  • 奖励答案:enumerate()。阅读文档;)
  • 实际上,for i in range(len(somelist)) 足以回答额外问题,因为它避免了明确的i += 1。但这仍然意味着您需要一个不必要的somelist[i],这正是enumerate 的重点,所以+1 给Joel Cornett。 (或者我可以列举他吗?)
  • 其实说的问题似乎不需要看任何元素:def getThisPattern(listWithALotOfValuesInit): return (True, True, False, True, True)。实际问题是否等同于str.find,而是list

标签: python list for-loop increment


【解决方案1】:

我会试一试:

subset = [True, True, False, True, True]
main = [False, True, False, True, True, True, False, True, True, False, True, False, True]

for i, j in  enumerate(xrange(len(subset), len(main) + 1)):
    if main[i:j] == subset:
        print subset, 'is at', i
        break
else:
    print 'not found'

注意:这有点粗暴,但一次性没问题...否则,看看尝试...

【讨论】:

    【解决方案2】:
    l = len(some_list) - 3
    for i, thing in enumerate(some_list):
        if i == l: break
        if thing and some_list[i+1] and not some_list[i+2] and some_list[i+3]:
            bingo(i)
    

    另一个未经测试的尝试:

    needle = [True, True, False, True, True]
    needle_len = len(needle)
    for i in range(len(stack)-needle_len+1):
        if needle == stack[i:i+needle_len]:
            bingo(i)
    

    【讨论】:

    • 这保证在i 到达len(some_list)-3 时立即抛出异常。
    • thing and (some_list[i+1] if i+1 < len(some_list) else True) and (...怎么样? (为了避免输入 3 次,我会编写一个包装它的函数……)
    • @abarnert (1) 这太令人震惊了。 (2) 我看不出以前的版本有什么问题。切片副本被传递给枚举,但列表没有被修改,所以some_listenumerate 看到的多了 3 个项目...
    • @delnan:我不确定您指的是哪个以前的版本。我写评论时那里的那个肯定是错误的。无论如何,现在有什么是有效的,但它与我建议的效果完全不同,所以你不能根据哪个更复杂来选择它们;您必须弄清楚哪一个适合您实际要解决的问题。 (换句话说,你想看看 some_list[-2] 即使它没有三个后续值,还是因为它没有三个后续值而自动失败?)
    • @abarnert 当点击“edited ago”链接时,我所指的版本被归档为“第一个”修订版。我并不是说你的建议与这个相比是可怕的(出于你引用的原因),我是说这是一段可怕的时期——必须有一种更易读的方法来实现这一点。
    【解决方案3】:

    如果我正确理解了这个问题,那么您正在尝试做的事情与str.find 完全相同,但在列表中查找子列表而不是字符串中的子字符串。

    如果是这样:

    def findSubList(l, sub):
      return (''.join('T' if x else 'F' for x in l)
              .find(''.join('T' if x else 'F' for x in sub)))
    

    这可能看起来很老套,但它是有道理的:你想在一个列表中找到一个子列表,两者都可以简单地转换为字符串,并且已经有一个内置函数可以在一个字符串中找到一个子字符串,那为什么不使用呢?

    它可能会变得太慢,但实际上,编写它的速度如此之快,值得对其进行测试,看看它是否太慢而无法满足您的目的。 (即使它太慢了,也可能是转换为字符串的部分很慢,首先使用字符串而不是布尔列表可能是合理的,或者在开始进行 200 次搜索之前转换一次,或其他任何东西。)

    如果这不可避免地太慢,我接下来要做的是查看str.find 的实现并将其转换为通用序列查找。你已经有了 Python 的源代码,所以这应该不会太难。但是,str.find 可能在 C 中,移植起来可能会很痛苦。在这种情况下,您可能仍想在线搜索纯 Python str.find……但假设您没有找到。

    下一步是查看 PyPI 上是否有序列查找模块。

    如果没有,有一些众所周知的算法要么易于实现,要么在 ActiveState 甚至 Wikipedia 等地方免费实现。请参阅 Wikipedia 上的 String searching algorithm 以获取列表和一些比较。 (性能特征取决于您没有提供给我们的因素——您需要进行多少次搜索;主要、子集或两者都不同;等等,这意味着没有更多信息,没有人能猜出哪个是最好的。)

    即使是标准的字符串搜索算法也可能不够快,并且利用您搜索单个位而不是 8 位字符这一事实可以大大提高效率。 (如果模式是固定的,您甚至应该能够将搜索描述为有限状态机,并将其转化为硬编码的、可证明的最优实现……)

    所以,您可能必须自己设计一些东西。但你不太可能这样做,如果可能的话,我会避免这样做。每当您遇到一个明显常见的问题时,值得假设它是一个已解决的问题并寻找解决方案,而不是试图从第一原则解决它。

    【讨论】:

      【解决方案4】:

      相当高效,使用滑动窗口方法,应该在线性时间内运行。

      def find_x_in_y(subset, main):
          """ Returns a list of the indexes of the first value of matches. """
          results = []
          for i in xrange(len(main)):
              if main[i:i+5] == subset:
                    results.append(i)
          return results
      
      # values borrowed from @JonClements
      subset = [True, True, False, True, True]
      main = [False, True, False, True, True, True, False, True, True, False, True, False, True]
      
      >>> find_x_in_y(subset, main)
      ... [4]
      

      @abarnert 很好,这是一个实际上非常有效的方法。

      效率极高,将整个 bool 列表转换为 bitarray 并运行原生搜索方法。

      from bitarray import bitarray
      
      def find_x_in_y(subset, main):
          subarray = bitarray(subset)
          mainarray = bitarray(main)
          return [int(i) for i in mainarray.itersearch(subarray)]
      

      timeit 结果:

      Length of main:                     10      100     1000    10000   100000   1000000
      
      returning _all_ matches:
      # number of matches                  1       10      100     1000    10000    100000
      # sliding window approach     (0.00059, 0.00502, 0.04194, 0.26211, 2.55554, 26.21962)
      # bitarray approach           (0.00028, 0.00072, 0.00484, 0.02926, 0.2822,   2.93676)
      
      returning first match:
      # sliding window approach     (0.00034, 0.00034, 0.00034, 0.00021, 0.00026,  0.00059)
      # bitarray approach           (0.00017, 0.00017, 0.00016, 0.00011, 0.00014,  0.00049)
      # joined string approach      (0.00134, 0.00721, 0.06244, 0.39224, 4.21628, 39.63207)
      

      【讨论】:

      • 这进行了 4 次不必要的比较:[False, True, False, True], [True, False, True], [False, True], [True]
      • 如果“应该在线性时间运行”你的意思是在 len(main) 中是线性的,是的,代码在线性时间运行,因为main[i:i+5] 将元素比较的数量限制为最多 5 个。如果您将 main[i:i+5] 更正为 main[i:i+len(subset)] 或等效项,那么很明显该方法是 O(len(main) * len(subset))。
      • @JonClements xrange(len(main)) 重写为 xrange(len(main)-4) 对于所谓的非常长的布尔值列表毫无意义。 :) @jwpat 仍然是线性的?我不明白你的意思。给我一点时间来进一步优化它。
      • @kreativitea:jwpat 的意思是它是O(NM),只有当M 是固定的/很小的时候它才是真正的线性。 Boyer-Moore 搜索是 O(N+M),如果 M 可能很大,这显然更好。
      • 同时,在不知道将如何使用的情况下讨论效率是没有意义的。是否会使用相同的main 但不同的subset 值多次调用它?使用相同的subset 但不同的main?两者不同?与main 相比,subset 总是很小吗?如果这两个值总是很小并且只运行一次,当然,性能甚至根本不重要,最好的答案是最容易读写的。
      猜你喜欢
      • 1970-01-01
      • 2021-01-22
      • 1970-01-01
      • 2011-07-03
      • 2020-10-23
      • 2010-09-24
      • 2010-09-24
      • 1970-01-01
      • 2016-04-01
      相关资源
      最近更新 更多