检查python列表是否遵循特定的重复模式

时间:2019-09-26 12:11:06

标签: python testing pattern-matching

我有一个仅包含两个符号的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']

任何与a,b中的l重复模式不同的内容都应视为已损坏。即使列表长度不均,它也会被破坏。因此,这必须是一个非常严格的测试。但实际上,如果列表l的长度为N,则意味着(a,b)必须重复自身N/2次。符号ab是将仅出现在这些列表中的内容,因此不需要进行检查,因为在此应用程序中它是不可想象的,因此可以看到其他任何内容。

我应该说,他们所有人都应该具有 first 模式。我正在寻找一种有效的方法,即测试,可以确定每个列表确实具有这种重复模式。如果没有抛出错误或类似的东西

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

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

编辑:

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

8 个答案:

答案 0 :(得分:3)

这是您需要检查的内容

列表中是否还有其他符号,并且列表的长度可以除以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]

如果它们全部通过,那就意味着您只有两个符号,并且两个后续符号永远不会相同

答案 1 :(得分:3)

性能

测试列表: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

答案 2 :(得分:2)

基于列表乘法的简单测试功能:

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

答案 3 :(得分:1)

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

答案 4 :(得分:1)

一种方法可能是先配对:

>>> 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.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,大写名称只能用于常量。

答案 5 :(得分:0)

您可以使用:

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: 

答案 6 :(得分:0)

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

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 :(得分:0)

仅且满足以下四个要求的列表是正确的

  • 列表中至少有一个条目
  • 最后一个条目是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内置方法)。

相关问题