简体   繁体   English

结构模式匹配 python - 依次匹配任意 position

[英]Structural pattern matching python - match at any position in sequence

I have a list of objects, and want to check if part of the list matches a specific pattern.我有一个对象列表,并想检查列表的一部分是否与特定模式匹配。

Consider the following lists:考虑以下列表:

l1 = ["foo", "bar"]
l2 = [{1, 2},"foo", "bar"]
l3 = ["foo", "bar", 5]
l4 = [{1,2},"foo", "bar", 5, 6]

How would I match the sequence ["foo", "bar"] in all the different cases?在所有不同的情况下,我将如何匹配序列["foo", "bar"]

My naive idea is:我天真的想法是:

match l4:
    case [*_, "foo", "bar", *_]:
        print("matched!")

Unfortunately this is a SyntaxError: multiple starred names in sequence pattern .不幸的是,这是一个SyntaxError: multiple starred names in sequence pattern The issue is, that I don't know how many elements are leading and trailing the pattern.问题是,我不知道有多少元素领先和落后于模式。

def struct_match(lst_target, lst_pattern):
    for i in range(len(lst_target)-(len(lst_pattern)-1)):
        if lst_target[i:i+len(lst_pattern)] == lst_pattern:
            print('matched!')
l1 = ["foo", "bar"]
l2 = [{1, 2},"foo", "bar"]
l3 = ["foo", "bar", 5]
l4 = [{1,2},"foo", "bar", 5, 6]
l5 = [{1,2},"foo", "baz", "bar", 5, 6]

patt = ["foo", "bar"]

struct_match(l1, patt)
struct_match(l2, patt)
struct_match(l3, patt)
struct_match(l4, patt)
struct_match(l5, patt)


# outputs

matched!
matched!
matched!
matched!

This cannot be done directly with pattern-matching, but pairing pattern matching with recursion works , though this is not really a good use case for structural pattern matching.这不能直接使用模式匹配来完成,但是将模式匹配递归配对是可行的,尽管这对于结构模式匹配来说并不是一个很好的用例。 We pattern match on the empty list or list of length one as an exit condition to break the recursion.我们在空列表或长度为 1 的列表上进行模式匹配作为退出条件来中断递归。

On each recursive call we slice the list to remove one element from the front.在每次递归调用中,我们对列表进行切片以从前面删除一个元素。

We are assuming seq is of length two.我们假设seq的长度为 2。 Without that this use of pattern matching becomes quite difficult.否则,这种模式匹配的使用将变得非常困难。

def has_seq(lst, seq):
  match lst:
    case [] | [_]: return False
    case [x, y, *_]: 
      if [x, y] == seq:
        return True
      else:
        return has_seq(lst[1:], seq)

It's important to remember that names in case patterns do not check to see if an element is equal to an existing variable.请务必记住,案例模式中的名称不会检查元素是否等于现有变量。 Rather they bind a name to that element.相反,它们将名称绑定到该元素。 If this has the same name as an existing variable, it shadows that variable.如果它与现有变量同名,它会隐藏该变量。

We can put a guard on a pattern to clean up the above.我们可以在一个模式上设置一个守卫来清理上面的内容。

def has_seq(lst, seq):
  match lst:
    case [] | [_]: return False
    case [x, y, *_] if [x, y] == seq: return True
    case _: return has_seq(lst[1:], seq)

A match can be used to match against literals.匹配用于匹配文字。 If we wanted to check if the first two elements of a list were 1 and 2 :如果我们想检查列表的前两个元素是否为12

match lst:
  case [1, 2, *_]: ...

But considering has_seq does not hardcode the values to look for, this cannot be utilized.但是考虑到has_seq没有硬编码要查找的值,因此无法使用。

Of course, I think this looks nicer using a generator expression and any to see if any subsequence of lst of the same length as seq is equal to seq .当然,我认为使用生成器表达式和any来查看与seq长度相同的lst的任何子序列是否等于seq看起来会更好。 This has the benefit of handling sequences of any length.这有利于处理任何长度的序列。

def has_seq(lst, seq):
  return any(lst[i:i+len(seq)] == seq 
             for i in range(len(lst) - len(seq) + 1))

Or, using in , just:或者,使用in ,只需:

def has_seq(lst, seq):
  return seq in (lst[i:i+len(seq)] == seq 
                 for i in range(len(lst) - len(seq) + 1)

Try this:尝试这个:

l1 = ["foo", "bar"]
l2 = [{1, 2},"foo", "bar"]
l3 = ["foo", "bar", 5]
l4 = [{1,2},"foo", "bar", 5, 6]

def locater(lst):
    trigger = False 
    i = 0
    while i < len(lst)-1:
        if l1[0] == lst[i] and l1[1] == lst[i+1]:
            trigger = True
            break
    return print('The pattern matches')
        

locater(l3)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM