Skip to main content

pattern matching - Successful match in Replace but not in Cases?


I have a little example that exhibits a successful pattern match in ReplaceAll that Cases misses and I wonder if the assembled sages would be so kind as to offer me an explanation?


This is chopped from an original Prolog emulator by Renan Cabrera circa 2000.


ClearAll[k,s,l,m,j,f,w,g,a,b]

Consider an unordered sequence of statements s stored in a knowledge base k, each being a predicate l about some terms m, f, j, and w:


k=s[l[m,f],
l[m,w],
l[j,w],

l[j,m]
];

We wish to search this knowledge base for any predicates such that l[m,X_] and l[j,X_], where X_ matches the same term in both predicates. Our first cut is something like


k /. s[l[m,X_],l[j,X_]] -> a[X]
(* Out[42]:= s[l[m,f],l[m,w],l[j,w],l[j,m]] *)

but this doesn't work until we make s flat and orderless (associative and commutative):


SetAttributes[s,{Flat,Orderless}]


and now


k /. s[l[m,X_],l[j,X_]] -> a[X]
(* Out[43]:= s[a[w],l[j,m],l[m,f]] *)

This is, of course, just the same as ReplaceAll[k, s[l[m,X_],l[j,X_]] -> a[X]]. (notice the canonical reordering in the output), and now I can extract my answer with something like


Cases[k /. s[ l[m, X_], l[j, X_] ]->a[X],a[Y_]->Y]
(* Out[44]:= {w} *)

The surprise and the question is why doesn't this work?


Cases[k, s[ l[m, X_], l[j, X_] ] -> a[X]]

(* Out[45]:= {} *)

or even this


MatchQ[k, s[ l[m, X_], l[j, X_] ]]
(* Out[46]:= False *)

It's as though ReplaceAll, Cases, and MatchQ are using different pattern matchers.


Will be grateful for advice, clues, discussion, etc.



Answer



MatchQ looks to see if the whole expression matches the pattern, so you need to add a blank to allow for the other elements of s:



MatchQ[k, s[l[m, X_], l[j, X_], ___]]

Cases takes a list and checks each element in turn, so I think the pattern matcher only gets to see each l[a,b] in isolation. The best I could come up with is:


Cases[Subsets[k, {2}], s[l[m, X_], l[j, X_]] -> a[X]]

Further investigation


Here are a couple of questions about Cases I have tried to answer experimentally. I don't know if this helps anyone else much, but it was useful for me to go through it and I think I now understand how Cases behaves, even if the why is a mystery.


Q1. Does Cases take into account Orderless ?


In[1]:= SetAttributes[a,{Orderless}]
In[2]:= Cases[a[x,y],a[y,_]->0,{0}]

Out[2]= {0}

A1. Yes, it does. The pattern matcher understands that a[x,y] === a[y,x] which matches the pattern.


Q2. Does Cases take into account Flat ?


In[3]:= SetAttributes[a,{Flat}]
In[4]:= Cases[a[x,y,z],a[onebit_,anotherbit_]:>{onebit+anotherbit},{0}]
Out[4]= {{a[x]+a[y,z]}}

A2. Yes, it does. The pattern matcher understands that a[x,y,z] === a[a[x],a[y,z]] which matches the pattern.


Q3. Given that Cases can use the above transformation, will it get a "hit" on the pattern a[x] ?



In[5]:= Cases[a[x,y,z],a[x]->b,{0,Infinity}]
Out[5]= {}

A3. No, even though the transformed expression a[a[x],a[y,z]] clearly contains a match for a[x] at level 1, this doesn't count. Cases appears to require the entire expression at level n to match the pattern. The logic appears to be:



  • Level 0: expression a[x,y,x] does not match a[x]

  • Level 0: transformed expression a[a[x],a[y,z]] does not match a[x]

  • Level 1: expression x does not match a[x]

  • Level 1: expression y does not match a[x]



etc...


This is in contrast to ReplaceAll, which does pick up the a[x] in the transformed expression:


In[6]:= a[x,y,z]/.a[x]->b
Out[6]= a[b,y,z]

So it seems like ReplaceAll applies Flat and Orderless transformations outermost, and then for each transformed expression it digs down into the subexpressions looking for a match. Whereas Cases digs down into the subexpressions of the untransformed original expression outermost, and for each subexpression it tries the various Flat and Orderless transformations looking for a match.


I realise this is probably a complete misrepresentation of how the pattern matcher actually works, but as a hand-waving mental picture it seems to explain the different behaviour of Cases and ReplaceAll


Comments

Popular posts from this blog

mathematical optimization - Minimizing using indices, error: Part::pkspec1: The expression cannot be used as a part specification

I want to use Minimize where the variables to minimize are indices pointing into an array. Here a MWE that hopefully shows what my problem is. vars = u@# & /@ Range[3]; cons = Flatten@ { Table[(u[j] != #) & /@ vars[[j + 1 ;; -1]], {j, 1, 3 - 1}], 1 vec1 = {1, 2, 3}; vec2 = {1, 2, 3}; Minimize[{Total@((vec1[[#]] - vec2[[u[#]]])^2 & /@ Range[1, 3]), cons}, vars, Integers] The error I get: Part::pkspec1: The expression u[1] cannot be used as a part specification. >> Answer Ok, it seems that one can get around Mathematica trying to evaluate vec2[[u[1]]] too early by using the function Indexed[vec2,u[1]] . The working MWE would then look like the following: vars = u@# & /@ Range[3]; cons = Flatten@{ Table[(u[j] != #) & /@ vars[[j + 1 ;; -1]], {j, 1, 3 - 1}], 1 vec1 = {1, 2, 3}; vec2 = {1, 2, 3}; NMinimize[ {Total@((vec1[[#]] - Indexed[vec2, u[#]])^2 & /@ R...

functions - Get leading series expansion term?

Given a function f[x] , I would like to have a function leadingSeries that returns just the leading term in the series around x=0 . For example: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x)] x and leadingSeries[(1/x + 2 + (1 - 1/x^3)/4)/(4 + x)] -(1/(16 x^3)) Is there such a function in Mathematica? Or maybe one can implement it efficiently? EDIT I finally went with the following implementation, based on Carl Woll 's answer: lds[ex_,x_]:=( (ex/.x->(x+O[x]^2))/.SeriesData[U_,Z_,L_List,Mi_,Ma_,De_]:>SeriesData[U,Z,{L[[1]]},Mi,Mi+1,De]//Quiet//Normal) The advantage is, that this one also properly works with functions whose leading term is a constant: lds[Exp[x],x] 1 Answer Update 1 Updated to eliminate SeriesData and to not return additional terms Perhaps you could use: leadingSeries[expr_, x_] := Normal[expr /. x->(x+O[x]^2) /. a_List :> Take[a, 1]] Then for your examples: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x), x] leadingSeries[Exp[x], x] leadingSeries[(1/x + 2 + (1 - 1/x...

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]