Pattern matching a pattern with patterns



Confusing title, I know. But the question is, if we have two patterns which have the same general structure but different names used in the patterns and different names:

 a = HoldPattern[f[x_, y_, g_, h_]] :> g[x] + h[y];
 b = HoldPattern[g[y_, z_, m_, l_]] :> m[y] + l[z];

And I would like to be able to define a pattern for these two pattens, letting {f,x,y,g,h} take arbitrary values. How would I go about this?

To clearify. If I had a=4;b=5. I could define a common pattern through: _Integer and get MatchQ[a,_Integer](*=>True*) and MatchQ[b,_Integer](*=>True*).

But for my above two patterns, I cannot simply base my pattenr on a and substitute out {f,x,y,g,h} with _ eg:

badpattern = HoldPattern[f_[Pattern[x_,_], Pattern[y_,_],
             Pattern[g_,_], Pattern[h_,_]]] :> h_[x_] + h_[y_];

I should not that what I want as a result is a working pattern not just a method that accomplishes this. Why? Well because the I would have to reimpliment MatchQ, Cases, Position and so forth for everything that expects a pattern as it's input to still work. The code below accomplishes this, however in an ad-hoc fasion.

This is wrong since the result does not distinguish between structural Blanks, and pattern blanks.

My initial code
Just to get a pattern to match to itself I need to get rid of HoldPattern which I do as:

MatchQ[a, a /. HoldPattern -> hp_ /; hp === HoldPattern]
(* True *)

Of cause I could just use Verbatim however then I won't be able to do the next part. Where I extend the same type of pattern of switching out pattern components such as HoldPattern.

To check b against a pattern based on a, I switch out a collection of heads inject new patterns and condition the pattern on the switched out heads:

   /.{f->f_,x-> x_,y-> y_,g-> g_,h-> h_ }

 (* True *)

Note that I'm just using a as a template of the new pattern I construct, in the new pattern {f,x,y,g,h} can all take arbitrary values since I'm injecting a new pattern for them after removing {HoldPattern,Pattern,Blank,RuleDelayed}.

It seems however that I'll occasionally have problems with other symbols, which means I need to extend the list for instance to include Set and SetDelayed, however only when they actually appear in the expression, since otherwise the check fails. As such it feels like a rather cumbersome method. So I'm wondering if others have dealt with such cases and how they have carried this out.


I should add that I'm relying on the matching to return values aswell, eg:

myPatternPattern = (a/.{HoldPattern->hp_,Pattern->p_,Blank->b_,RuleDelayed->rd_}
       /.{f->f_,x-> x_,y-> y_,g-> g_,h-> h_ }


Mr. Wizard inquired as to what exactly I mean by the two definitions having the same structure. So I'll clarify. If we have two different expressions: f[3,4] and g[2,5] and I wanted to describe their structure, then I could write name_[_Integer,_Integer] as one possible common structure. When I have two patterns this approach breaks down for obvious reasons, Consider; _[varA_] and _[varB_]. I would like to define a "pattern^2" that matches both of these patterns and assigns varA or varB to a name, so I just follow the same recipe as before and substitute varA in the first with name_ and get: `_[_name_]. This obviously fails. So what I have above is a way to define a pattern for patterns such that I can match elements inside them. Confusing, I know.


Posted 2013-01-16T12:54:55.097

Reputation: 14 423

1I have a small problem with your specification because f =!= g. How can these be considered the same? If they are, what else is too? – Mr.Wizard – 2013-01-16T15:32:39.307

@Mr.Wizard I don't follow you, f and g are never considered the same. When matching the pattern f->f_ replaces the symbol f with a pattern that (maybe confusingly so) is called f. You could equally well write f->x_ and then have x match f and g in each respectively. – jVincent – 2013-01-16T15:36:09.833

What you ask for in your update is a pattern analog of so-called parametrized types (exist in ML family of languages, for example). While very interesting and potentially useful thing, this is IMO quite different from your initial question. – Leonid Shifrin – 2013-01-16T15:51:17.230

In your example f and g are not patterns, they are literals. – Mr.Wizard – 2013-01-16T15:51:20.480

@LeonidShifrin My clearification has nothing to do with types, I just chose those, since they are the type of structure you will typically see in a Pattern. I'm editing to remove the unwanted connection. – jVincent – 2013-01-16T15:53:42.440

@Mr.Wizard I substitute the literals for patterns after "breaking apart" other patterns by switching out the heads Pattern, Blank and such. – jVincent – 2013-01-16T15:56:07.370

I swear I'm not trying to be obtuse, but I still don't understand where you specify how f and g in the two patterns are supposed to be equivalent. – Mr.Wizard – 2013-01-16T15:59:32.863

@Mr.Wizard in the 3'rd codebox. I write MatchQ[b, (a /. {HoldPattern -> hp_,...} /. {f -> f_,...}) /; And[hp === HoldPattern,...]] Now the "f-> f_" replaces the literal f with a pattern. The method goes, 1: break down all patterns, 2: inject new patterns; 3: Verify that all the structure of the broken down patterns is still correct. – jVincent – 2013-01-16T16:14:18.140

So you're not trying to check if the two pattern are equivalent, but rather do some transformation using them? – Mr.Wizard – 2013-01-16T16:16:35.740

@Mr.Wizard I'm using my "patterns for patterns" in things such as Cases, however I wouldn't call that transformations using the patterns. The question is really just about using pattern matching to match things that are themselves patterns. – jVincent – 2013-01-16T16:20:23.113

Could you join me in Chat? – Mr.Wizard – 2013-01-16T16:23:57.870

1Have to leave now for today, but my final feeling for all this is that the question is really too vague. There are 3 answers currently, and each interpreted it differently. Unupvoting. – Leonid Shifrin – 2013-01-16T16:31:45.627

@LeonidShifrin I disagree. You seemed to interpret my question correctly, only you didn't fully answer it and you had an initial error which prevented me from getting your method to work, so I posted my own. Mr. Wizard did misinterpret the question, however he seemed to have skipped the part of the question that said letting {f,x,y,g,h} take arbitrary values, and attempted to answer the question ignoring this part even after some effort on my part to clarify this for him. Having one answer that misinterprets the question is hardly makes it vague. I have attempted to clarify by restructuring. – jVincent – 2013-01-16T21:23:21.200

You are free to disagree, as well as I am free to express my opinion :) I fully answered the part which was well-defined, in my opinion. But since you are the one to decide which answer is best for you, all I can say is that IMO the question was vague, and that's what I already did. If at the end you got the information you wanted, that's all that matters at present, but stating question in a more precise fashion would make it easier for us, as well as more useful for future visitors. – Leonid Shifrin – 2013-01-16T23:15:12.553



From a short discussion in Chat I think this question is going in a different direction. It may be a duplicate of How to match a pattern with a pattern? but perhaps more complicated than the examples there.

To illustrate how a pattern can be fully broken down:

a = HoldPattern[g[x_, y_, _, {1, 2}, q : {g_, h_}]] :> g[x] + h[y];

meta =
   HoldPattern @@ {a},
   {Pattern -> Verbatim[Pattern], Blank -> Verbatim[Blank], HoldPattern -> Verbatim[HoldPattern]},
   {2, -1},
   Heads -> True

MatchQ[a, meta]


HoldPattern @@ {a} and {2, -1} is used to keep an outer HoldPattern around the entire thing to prevent any unintentional evaluations.

Every part of meta can be tagged with patterns and used as normal.

Since it doesn't make sense to me to assert that f and g literals are similar I shall ignore that for now. Consider this:

fill[pat_] := Range@Length@# /. # -> Extract[pat, 2, Hold] & @ 
   Cases[pat[[1]], Quiet[Verbatim[Pattern][name_, _] :> name_], -1];

similar[a_, b_] := And[
  SameQ @@ ({a, b}[[All, 1]] //. Verbatim[Pattern][_, x_] :> x),
  SameQ @@ fill /@ {a, b}


a = HoldPattern[f[x_, y_, g_, h_]] :> g[x] + h[y];
b = HoldPattern[f[y_, z_, m_, l_]] :> m[y] + l[z];

similar[a, b]



Posted 2013-01-16T12:54:55.097

Reputation: 259 163

The possible duplicate is not relevant to this. The question there was to for instance determine that a_Integer and b_Integer span the same space. Here I would like to for instance have a pattern that spiritually reads (a|b)_(Integer|List) So I have a pattern that I want to use on the structure of a different pattern. That being said. I do belive that you have a real solution in injecting Verbatim around pattern components rather than my attempt at removing and checking these components. – jVincent – 2013-01-16T21:27:09.170

I have submitted an answer based on your method which solves my problems an allows the definition of a pattern for patterns without introducing a new function to handle comparison. I feel it's bascially a small rewrite of your method (at least your input in chat), so if you would like to include it in your answer I'll delete mine. – jVincent – 2013-01-16T21:48:40.603


Here is one possibility. Comparing patterns is a special case of comparing trees in general, so we need some specialized version of diff of Mathematica expressions. One way to build such a diff is to "serialize" expression into atomic elements and then compare the serialized forms.

I will use slightly more complex patterns as examples:

a = HoldPattern[g[x_, y_, _, {1, 2}, q : {g_, h_}]] :> g[x] + h[y];
b = HoldPattern[g[y_, z_, _, {1, 2}, p : {m_, l_}]] :> m[y] + l[z];

Note that the well-defined problem is when the difference is only in pattern names, so I consider the function's name to be the same. One can probably extend the solution below to also different function names, but one would need to impose more constraints on what is considered same, then.

Here is the serialization code:

patternSerialize[pt_] :=
      MapIndexed[List, pt, {0, Infinity}], 
      s_ /; AtomQ[Unevaluated[s]] :> HoldComplete[s], 
      {0, Infinity}, 
      Heads -> True];

so that, for example:



Now, the next ingredient is to align the serialized sequences, and here we will leverage SequenceAlignment:

patternsAlign[pt1_, pt2_] :=
   SequenceAlignment @@ Map[patternSerialize, {pt1, pt2}];

so that, for example (note: this example refers to a previous version of the code, but the new corrected one will produce much longer output which is hard to understand, while the essence remains mostly the same):


       HoldComplete[1], HoldComplete[2],HoldComplete[Pattern]},

I intentionally gave here the full output, so that you can see the structure. Here is then the function which attempts to establish the fact of the pattern's equality and record the transformation rules for pattern names at the same time:

patternsSameQ[{}] := True;
patternsSameQ[pt1_HoldPattern, pt2_HoldPattern] :=
   patternsSameQ[patternsAlign[pt1, pt2]];
patternsSameQ[{{Except[_List] .., HoldComplete[Pattern],HoldComplete[List]}, 
     {{HoldComplete[x_Symbol]}, {HoldComplete[y_Symbol]}},
     rest___}] :=
  (Sow[HoldPattern[x] :> y, $tag]; patternsSameQ[{rest}]);
patternsSameQ[{{Except[_List] ..}, rest___}] :=
patternsSameQ[__] := False;

Here is what we get for our case:



Now, the final function: it will collect these rules and apply those to the r.h.s. of the first rule, and compare to the second one:

rulesEquivalentQ[lfst_ :> rfst_, lsec_ :> rsec_] :=
   Module[{psame = Reap[patternsSameQ[lfst, lsec], $tag, #2 &]},
     (rfst /. If[# === {}, {}, First[#]] &@Last[psame]) === rsec /;First[psame]
rulesEquivalentQ[__] := False

This gives:


(*  True  *)

Leonid Shifrin

Posted 2013-01-16T12:54:55.097

Reputation: 108 027

I'm not sure why you feel the need to explicitly split up into lhs/rhs. Why not just match the entire patterns? – jVincent – 2013-01-16T14:25:06.680

@jVincent Because then, the r.h.s will induce differences reflected as nested lists in alighned sequence, and I'd need to distinguish between those which resulted from patterns and those which resulted from r.h.sides. This would add needless complexity. Besides, the r.h.sides can be quite large/involved, so it would be less efficient as well. Finally, I always try to reuse the highest-level constructs (rule application here), since then there are less chances for errors. Overall, I just think it is cleaner to do it this way. – Leonid Shifrin – 2013-01-16T14:28:43.043

It might be cleaner, but it won't work in general for searching through patterns, only those that happen to take the form of DelayedRules, unless I'm misunderstanding something? Also, how would you go about extracting elements using your method(see edit)? – jVincent – 2013-01-16T14:30:43.247

@jVincent Sorry, I have no time to address this in full at the moment. It seems to be a rather trivial matter to generalize my solution to address the needs you specified. I started from the expressions you gave as examples, but generalizations are rather straightforward. The main method I presented here is quite general, that's one thing I am sure about. You'd need to be more precise in exactly what extended behavior you want, to generalize my specific code in a meaningful / unambigious/ well defined way. I will come back to it later when I have time, unless someone else does that first. – Leonid Shifrin – 2013-01-16T14:48:29.900

I have a problem with the serialization. It'll give the same for eg. a[b,c] and a[b@c], so your solution gives true when testing with a = HoldPattern[g[x_@y_, _, {1, 2}, q : {g_, h_}]] :> g[x] + h[y] and the same b. – jVincent – 2013-01-16T15:49:41.567

@jVincent Yes, this is a good point. I was too careless. Please see the edit. – Leonid Shifrin – 2013-01-16T16:14:40.977


After a chat with Mr. Wizard I finally had the breakthrough I was looking for, which is how to get this to work using Verbatim. I had already tried to do this but my attempts where somewhat misguided.

The solutoin; you can simply take a template pattern, then apply Verbatim to all sensitive Heads, and inject any wanten patterns in place of previous literals:

 shield = {HoldPattern, Blank, Pattern, RuleDelayed};
   substitutionRules = (# -> Verbatim@#) & /@ shield;
   structural[pattern_] := pattern /. substitutionRules

So now, given my two patterns:

a = HoldPattern[f[x_, y_, g_, h_]] :> g[x] + h[y];
b = HoldPattern[g[y_, z_, m_, l_]] :> m[y] + l[z];

I can define a pattern for patterns simply by applying structural to a template and injecting the patterns I would like into the resulting literal match to create a pattern-pattern:

myPatternPattern = structural[
HoldPattern[name[p1_, p2_, p3_, p4_]] :> p3[p1] + p4[p2]
] /. {name -> name_, p1 -> p1_, p2 -> p2_, p3 -> p3_, p4 -> p4_}

MatchQ[a, myPatternPattern]
MatchQ[b, myPatternPattern]

And this seamlessly works everywhere else (Cases,DeleteCases,Position etc.):

Cases[{a, b}, myPatternPattern :> name]
(* {f,g} *)

All credit goes to Mr.Wizard.


Posted 2013-01-16T12:54:55.097

Reputation: 14 423