【问题标题】:Check that python list follows a specific repeating pattern检查 python 列表是否遵循特定的重复模式
【发布时间】:2019-09-26 12:11:06
【问题描述】:

我有一个仅包含两个符号的 python 列表,假设它们是 ab,列表如下所示:

l = ['a','b','a','b','a','b','a','b','a','b','a','b','a','b','a','b']

现在在我的应用程序中,我有数千个这样的列表,它们的长度各不相同(通常有几百长)。但它们的共同点是它们具有重复模式(a,b)。例如,此列表已损坏:

l_broken = ['a','b','b','a','a','b','a','b','a','a','a','b','a','b','b','a']

任何偏离l 中的a,b 重复模式的东西都必须被视为损坏。即使列表的长度不均匀,它也会被破坏。所以这一定是一个非常严格的测试。但本质上,如果列表l 的长度为N,则意味着(a,b) 必须重复N/2 次。符号 ab 是唯一会出现在这些列表中的东西,因此不需要检查它,因为它在这个应用程序中是不可想象的,其他任何东西都可以看到。

我应该说他们都应该first 模式。我正在寻找一种有效的方法,一种测试,它可以确定每个列表实际上确实具有这种重复模式。如果不抛出错误或其他东西,例如

assert my_fancy_test(l), 'the list does not follow the correct pattern'

我想我正在寻找子序列匹配,但我的 google 搜索不足。

编辑:

感谢大家的出色解决方案。我不知道你能做到这些的一半。好东西。我在最后添加了一个简短的性能概述供您仔细阅读。

【问题讨论】:

  • one_list in another_list?
  • @OlvinR​​oght 你可以做assert [a,b] in L,但我需要知道这种模式实际上在整个列表中重复。
  • @RomanPerekhrest 不确定您的意思,我的列表仅包含两个符号,它们以上述模式重复。
  • 实际的符号是字符串吗?还是你只是用它们来代表问题?
  • @tituszban 它们是实际的字符串,但如果这样更容易,我很乐意用其他东西替换。它们只是实际发生的操作的占位符。

标签: python testing pattern-matching


【解决方案1】:

性能

测试列表:l = ['a','b']*100000

RomanPerekhrest的解决方案:

CPU times: user 636 µs, sys: 0 ns, total: 636 µs
Wall time: 639 µs

GZ0的回答:

CPU times: user 14.6 ms, sys: 78 µs, total: 14.7 ms
Wall time: 13.9 ms

Silveris' 回答:

CPU times: user 95.2 ms, sys: 3.95 ms, total: 99.1 ms
Wall time: 98 ms

h4z3的回答:

CPU times: user 39.9 ms, sys: 0 ns, total: 39.9 ms
Wall time: 38.6 ms

tituszban的回答:

CPU times: user 2.71 ms, sys: 46 µs, total: 2.76 ms
Wall time: 2.76 ms

ruso_ro1回答:

CPU times: user 32.4 ms, sys: 3.35 ms, total: 35.8 ms
Wall time: 34.7 ms

Elias Strehle

CPU times: user 11.7 ms, sys: 0 ns, total: 11.7 ms
Wall time: 12.1 ms

【讨论】:

  • Psst,这非常有用,但如果您已链接到答案,它会更有用。
  • 我不知道你能做到这一点......等等,让我弄清楚。
  • 在答案上点击分享获取直接链接,并使用编辑器中的链接
  • @Astrid 检查我的解决方案
  • @rusu_ro1 我不知道那里发生了什么,它现在可以工作了,我添加了性能。
【解决方案2】:

这是您需要检查的内容

您的列表中是否还有其他符号,并且您的列表长度可以被 2 整除:

assert len(l) == l.count('a') + l.count('b') and len(L) % 2 == 0

并且没有重复的符号:

j = ''.join(l)
assert 'aa' not in j and 'bb' not in j

列表中的第一项是'a'

assert 'a' is L[0]

如果它们都通过了,那一定意味着你只有两个符号,而后面的两个符号永远不会相同

【讨论】:

  • 我不确定在 i 上迭代 3 次,然后在 j 上迭代 2 次是一种非常有效的方法。
  • @IgnacioVergaraKausel 这是继RomanPerekhrest's 之后第二个最有效的解决方案。它实际上比下一个效率高 4 倍,下一个是 h4z3
  • 这将错误地将['b', 'a', 'b', 'a'] 验证为未损坏,因为它不符合 OP 要求的 (a, b) 模式,因此它应该被视为损坏。
【解决方案3】:

基于列表乘法的简单测试函数:

def test_repeating_pattern(lst, pat):
    pat_len = len(pat)
    assert len(lst) % pat_len == 0, 'mismatched length of list'
    assert list(pat) * (len(lst) // pat_len) == lst, 'the list does not follow the correct pattern'
    print(lst, 'is valid')


L = ['a','b','a','b','a','b','a','b','a','b','a','b','a','b','a','b']
L_broken = ['a','b','b','a','a','b','a','b','a','a','a','b','a','b','b','a']

测试:

test_repeating_pattern(L, ('a', 'b'))
['a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b'] is valid

test_repeating_pattern(L_broken, ('a', 'b'))
AssertionError: the list does not follow the correct pattern

【讨论】:

    【解决方案4】:
    def my_fancy_test(my_list):
        pattern = ['a', 'b']
        if not len(my_list) % len(pattern) == 0:
            return False
        for i in range(0, len(my_list)):
            if not my_list[i] == pattern[i % len(pattern)]:
                return False
        return True
    

    模式可以是任意长度的任意列表(通用解决方案)。

    只检查完整的模式(例如 a,b,a 将失败)并且模式必须从头开始(例如 b,a,b 也会失败)。

    L = ['a','b','a','b','a','b','a','b','a','b','a','b','a','b','a','b']
    assert my_fancy_test(L) #passes
    L2 = ['a','b','a','b','a','b','a','b','a','b','a','b','a','b','a','c']
    assert my_fancy_test(L2) #fails
    

    【讨论】:

    • 你传入 my_list,但不是引用 L
    • 糟糕,一个错字。我在本地测试它,而不是在函数中。编辑:固定^^
    【解决方案5】:

    一种方法是先配对:

    >>> l = ['a','b','a','b','a','b','a','b','a','b','a','b','a','b','a','b']
    >>> pairs = [[l[i], l[i + 1]] for i in range(0, len(l) - 1, 2)]
    >>> pairs
    [['a', 'b'], ['a', 'b'], ['a', 'b'], ['a', 'b'], ['a', 'b'], ['a', 'b'], ['a', 'b'], ['a', 'b']]
    

    然后统计['a', 'b']在pairs列表中出现的次数,并与列表大小的一半进行比较:

    >>> pairs.count(['a', 'b']) == len(l) / 2
    True
    

    看起来像:

    def my_fancy_test(l):
        pairs = [[l[i], l[i + 1]] for i in range(0, len(l) - 1, 2)]
        return pairs.count(['a', 'b']) == len(l) / 2
    

    PS:请注意,根据 PEP8,大写名称只能用于常量。

    【讨论】:

    • 对全局变量约定一无所知。谢谢,会修改问题。
    • 对不起,我指的是常量,而不是全局变量...如果您想了解更多信息:python.org/dev/peps/pep-0008
    【解决方案6】:

    另一种方法是加入列表并使用正则表达式进行模式匹配:

    import re
    
    def is_broken(input_list, pattern = re.compile("(?:ab)*")):
        return pattern.fullmatch(''.join(input_list)) is None
    
    print(is_broken(['a','b','a','b','a','b','a','b','a','b','a','b','a','b','a','b']))
    print(is_broken(['a','b','b','a','a','b','a','b','a','a','a','b','a','b','b','a']))
    

    输出:

    False
    True
    

    这种方法也可以用来高效匹配一些更复杂的模式,或者在匹配模式的同时提取信息。

    【讨论】:

      【解决方案7】:

      你可以使用:

      pattern = ['a', 'b']
      
      p_len = len(pattern)
      assert all(pattern == L[i: i + p_len] for i in range(0, len(L), p_len))
      

      您逐块获取模式大小并针对模式进行测试

      例如:

      pattern = ['a', 'b']
      
      p_len = len(pattern)
      l_broken = ['a','b','b','a','a','b','a','b','a','a','a','b','a','b','b','a']
      all(pattern == l_broken[i: i+p_len] for i in range(0, len(l_broken), p_len))
      

      输出:

      ---------------------------------------------------------------------------
      AssertionError                            Traceback (most recent call last)
      <ipython-input-160-edf435d855b9> in <module>
            1 p_len = len(pattern)
            2 l_broken = ['a','b','b','a','a','b','a','b','a','a','a','b','a','b','b','a']
      ----> 3 assert all(pattern == l_broken[i: i+p_len] for i in range(0, len(l_broken), p_len))
      
      AssertionError: 
      

      【讨论】:

      • 您应该检查现有答案。 RomanPerekhrest 已经发布了。
      • 当然。他在一个函数中。但在功能上是完全一样的。重要的是功能,而不是打包方式。
      • 是的,明显的性能差异是 0.29%(在 160000000 长度列表上)
      【解决方案8】:

      一个列表正确当且仅它满足以下四个要求:

      • 列表至少有一个条目
      • 最后一个条目是b
      • 具有偶数索引的每个条目都是a
      • 具有奇数索引的每个条目都是b
      def is_correct_entry(idx, v):
          if idx % 2 == 0:
              return v == 'a'
          else:
              return v == 'b'
      
      def my_fancy_test(l):
          return l and l[-1] == 'b' and all(is_correct_entry(idx, v) for idx, v in enumerate(l))
      

      此代码可能具有速度优势,因为它不会创建新列表,避免昂贵的 len 计算,并且如果发现不正确的条目,则不会检查列表的其余部分(检查 documentation 以获取 all内置方法)。

      【讨论】:

      • l = ['b', 'a'] + ['a', 'b'] * 100000 测试它;-) 感谢有趣的脑筋急转弯!
      • 哈,这很有趣:CPU times: user 6 µs, sys: 0 ns, total: 6 µs; Wall time: 7.63 µs -- 那么这里发生了什么?
      • 一看到第一个条目 ('b'),您就知道该列表一定是错误的。所以你可以停止检查并返回False。这就是我的代码使用all 方法所做的事情。所有其他解决方案都以某种方式检查整个列表,即使第一个条目已经不正确。
      • 啊啊,真是聪明。
      • @EliasStrehle rusu_ro1 的解决方案不会检查整个列表。从技术上讲,我的解决方案也不会 check 整个列表,但它必须从列表中构建一个字符串,因此您可以说它需要遍历整个列表。如果输入最初来自一个完整的字符串,那么可以避免这种情况。
      猜你喜欢
      • 1970-01-01
      • 2020-09-27
      • 2020-09-20
      • 2014-12-29
      • 2012-01-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多