Fazendo uma alternativa mais rápida para {PatternSequence [1, PatternSequence [2, 3 ..] ..] ..}

Sep 23 2020

Preciso melhorar um padrão ou mudar uma abordagem.

É melhor descrito por um exemplo

Para uma hierarquia / ordem fornecida por uma lista, por exemplo:

order = {1, 2, 3} 

e uma lista:

list = {
  1, 2, 3, 2, 3, 3, 2, 3, 3, 2, 3, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 3,
   3, 3, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3, 3, 3
  }

Preciso verificar se listcorresponde a uma sequência definida por order:

MatchQ[list, {PatternSequence[1, PatternSequence[2, 3 ..] ..] ..}]

Este padrão escala muito mal, já que não se vai terminar de avaliar.

A função deve ter apenas listcomo argumento, considere a constante de ordem. O padrão não precisa ser construído automaticamente.

Respostas

10 LeonidShifrin Sep 23 2020 at 22:53

O seguinte parece funcionar para mim, a menos que esteja faltando alguma coisa:

ClearAll[match]
match[{}][{}] := True;
match[{fst_, rest___}][l_List] :=
  And @@ Map[
    Replace[
      match[{rest}][#], 
      False :> Return[False, Map]
    ]&,
    Replace[
      ReplaceList[
        l, 
        {
          {___, fst, middle : Except[fst] ..., fst, ___} :> {middle}, 
          {___, fst, r : Except[fst] ...} :> {r}
        }
      ],
      {} -> False
    ]
 ]

(A peça Replace[match[{rest}][#], False :> Return[False, Map]]&é opcional e pode, em princípio, ser substituída por apenas match[{rest}]).

Exemplo:

match[{1, 2, 3}][list] // AbsoluteTiming
match[{1, 2, 3}][Append[list, 1]] // AbsoluteTiming

(* {0.00038, True} *)

(* {0.000383, False} *)
10 C.E. Sep 24 2020 at 02:44

Esta solução tenta reduzir a lista a uma lista de um único tipo de elementos; se tiver êxito, a lista segue o padrão prescrito.

MatchQ[
  SequenceReplace[
   SequenceReplace[list, {2, 3 ..} :> x],
   {1, x ..} :> y
   ],
  {y ..}
  ] // AbsoluteTiming

{0.0019598, True}

Esta é uma visão da máquina de estado que Daniel recomendou em um comentário:

f[1, 2] = 2;
f[2, 3] = 3;
f[3, 2] = 2;
f[3, 1] = 1;
f[3, 3] = 3;
f[_, _] := Throw[False]

And[
  First[list] == 1 && Last[list] == 3,
  Catch[Fold[f, list]; True]
] // AbsoluteTiming

{0.0000455, True}