Skip to main content

Big and Little surprises when unrolling tests of pattern-matching and attributes


Related question concerning interpretation of the substitutions:


Subtle order-of-evaluation issues when pattern-matching with attributes


Questions researched before posting this one:



Orderless pattern matching


Combinations of multiple matching patterns


About OneIdentity


What has changed in pattern matching functions with the Orderless attribute?


Transformation rule for arbitrary number of argument expressions


Pattern does not match with Orderless head


All work done with Mathematica 11.3.0.0 on Ubuntu 18.04 (bionic)


In an attempt to understand pattern-matching better, I tried some exhaustive testing of a certain substitution rule under all seven combinations of attributes, leaving out the null case of no attributes:


In[1]:= allAtts =
Flatten[Table[

Union[Sort /@
Permutations[{Flat, Orderless, OneIdentity}, {i}]], {i, 3}], 1]
Out[1]= {{Flat}, {OneIdentity}, {Orderless}, {Flat, OneIdentity}, {Flat,
Orderless}, {OneIdentity, Orderless}, {Flat, OneIdentity,
Orderless}}

The substitution rule attempts to match the pattern eqv[x_, y_] against the input eqv[p, q, r] to see what gets bound to x and y for each combination of attributes.


In the first test, I define the substitution before setting the attributes. After some manual prettification:


In[2]:= Table[Module[{e = (eqv[p, q, r] /. {eqv[x_, y_] :> {x, y}})},
ClearAll[eqv];

SetAttributes[eqv, j];
{j, First@e, Rest@e}],
{j, allAtts}]
Out[2]= {{{Flat}, p, eqv[q, r]},
{{OneIdentity}, eqv[p], {eqv[q, r]}},
{{Orderless}, p, eqv[q, r]},
{{Flat, OneIdentity}, p, eqv[q, r]},
{{Flat, Orderless}, p, {eqv[q, r]}},
{{OneIdentity, Orderless}, q, {eqv[p, r]}},
{{Flat, OneIdentity, Orderless}, p, eqv[q, r]}}


The results are reasonable, plausible, interpretable. I won't go into interpreting the results in this post other than to note that the results are subtly different when attributes are set before the substitution rule is parsed. The subject of this post is that unrolling the tests produce very different, not subtly different, results. Because I unroll below under both conditions --- substitution parsed before attributes set and attributes set before substitution parsed --- here are the results of the latter, subtly different:


In[3]:= Table[Module[{e},
ClearAll[eqv];
SetAttributes[eqv, j];
e = (eqv[p, q, r] /. {eqv[x_, y_] :> {x, y}});
{j, First@e, Rest@e}],
{j, allAtts}]
Out[3]= {{{Flat}, eqv[p], {eqv[q, r]}}, (* difft *)
{{OneIdentity}, p, eqv[q, r]}, (* difft *)

{{Orderless}, p, eqv[q, r]},
{{Flat, OneIdentity}, p, {eqv[q, r]}}, (* difft *)
{{Flat, Orderless}, q, {eqv[p, r]}}, (* difft *)
{{OneIdentity, Orderless}, p, eqv[q, r]}, (* difft *)
{{Flat, OneIdentity, Orderless}, p, {eqv[q, r]}}}

In a separate post, I have questions about interpreting the results. I Continue here with the main question about unrolling.


I now attempt to unroll these tests, one test for each combination of attributes rather than one table with all combinations. I get many surprises that reveal I don't really understand what's going on. I highlight these surprises below and would be grateful for explanation and clarification.


First, here is my attempt at unrolling the tests:


In[3]:= test32substBeforeAttrs[attrs_] := Module[{eval},

eval[] := eqv[p, q, r] /. {eqv[x_, y_] :> {x, y}};
ClearAll[eqv]; SetAttributes[eqv, attrs]; eval[]]

I try to prevent any early evaluation of the substitution by packaging it in a delayed rule named eval, but I do want the substitution denoted and parsed before attributes are set, which (I thought), were the same conditions as in the rolled-up test.


Surprise number one, the Flat results look similar to the test where attributes are set before the substitution rule is parsed, just without a list enclosing the second binding for y:


In[5]:= test32substBeforeAttrs[{Flat}]
Out[5]= {eqv[p], eqv[q, r]}

Surprise number two, we get no matches for OneIdentity alone and for Orderless alone:


In[6]:= test32substBeforeAttrs[{OneIdentity}]

Out[6]= eqv[p, q, r]
In[7]:= test32substBeforeAttrs[{Orderless}]
Out[7]= eqv[p, q, r]

No surprises for {Flat, OneIdentity}:


In[8]:= test32substBeforeAttrs[{Flat, OneIdentity}]
Out[8]= {p, eqv[q, r]}

but surprise number three for {Flat, Orderless}: p and q are reversed, just like the rolled-up case for {OneIdentity, Orderless}, except, again, no list enclosing the second binding, the binding for y:


In[9]:= test32substBeforeAttrs[{Flat, Orderless}]

Out[9]= {q, eqv[p, r]}

Surprise number four: no match for unrolled {OneIdentity, Orderless}:


In[10]:= test32substBeforeAttrs[{OneIdentity, Orderless}]
Out[10]= eqv[p, q, r]

No surprise for the case of all attributes.


In[11]:= test32substBeforeAttrs[{Flat, OneIdentity, Orderless}]
Out[11]= {p, eqv[q, r]}


Now, my attempt at an unrolled setting of attributes before denoting and parsing the substitution rule. Again, I attempt to prevent early evaluation by packaging my main substitution rule in an outer, delayed substitution rule named eval:


In[12]:= test32AttrsBeforeSubst[attrs_] := Module[{eval},
ClearAll[eqv]; SetAttributes[eqv, attrs];
eval[] := eqv[p, q, r] /. {eqv[x_, y_] :> {x, y}};
eval[]]

In[13]:= test32AttrsBeforeSubst[{Flat}]
Out[13]= {eqv[p], eqv[q, r]}
In[14]:= test32AttrsBeforeSubst[{OneIdentity}]
Out[14]= eqv[p, q, r]

In[15]:= test32AttrsBeforeSubst[{Orderless}]
Out[15]= eqv[p, q, r]
In[16]:= test32AttrsBeforeSubst[{Flat, OneIdentity}]
Out[16]= {p, eqv[q, r]}
In[17]:= test32AttrsBeforeSubst[{Flat, Orderless}]
Out[17]= {q, eqv[p, r]}
In[18]:= test32AttrsBeforeSubst[{OneIdentity, Orderless}]
Out[18]= eqv[p, q, r]
In[19]:= test32AttrsBeforeSubst[{Flat, OneIdentity, Orderless}]
Out[19]= {p, eqv[q, r]}


The big surprise here is that the results are exactly the same as the unrolled test with substitution parsed before attributes set.


To summarize, in addition to the four little surprises when unrolling the tests, there is a big surprise that the relative order of denoting the rule matters when the tests are rolled up and not when the tests are unrolled.


I've managed to confuse myself mightily, here, and would be grateful for insights from those who understand both the big picture and the micro details better than I do. I apologize for the length and complexity of this question, but I made it as short and as simple as I know how to. The gist of this question is that I don't know nearly as much as I thought I did about pattern-matching and attributes. Perhaps after I learn more from you-all, a much simpler form of the essential question --- hiding in here somewhere I hope --- will emerge.




Comments

Popular posts from this blog

plotting - How to draw lines between specified dots on ListPlot?

I would like to create a plot where I have unconnected dots and some connected. So far, I have figured out how to draw the dots. My code is the following: ListPlot[{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4,13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full] I have thought using ListLinePlot command, but I don't know how to specify to the command to draw only selected lines between the dots. Do have any suggestions/hints on how to do that? Thank you. Answer One possibility would be to use Epilog with Line : ListPlot[ {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4, 13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full, Epilog -> { Line[ ...

dynamic - How can I make a clickable ArrayPlot that returns input?

I would like to create a dynamic ArrayPlot so that the rectangles, when clicked, provide the input. Can I use ArrayPlot for this? Or is there something else I should have to use? Answer ArrayPlot is much more than just a simple array like Grid : it represents a ranged 2D dataset, and its visualization can be finetuned by options like DataReversed and DataRange . These features make it quite complicated to reproduce the same layout and order with Grid . Here I offer AnnotatedArrayPlot which comes in handy when your dataset is more than just a flat 2D array. The dynamic interface allows highlighting individual cells and possibly interacting with them. AnnotatedArrayPlot works the same way as ArrayPlot and accepts the same options plus Enabled , HighlightCoordinates , HighlightStyle and HighlightElementFunction . data = {{Missing["HasSomeMoreData"], GrayLevel[ 1], {RGBColor[0, 1, 1], RGBColor[0, 0, 1], GrayLevel[1]}, RGBColor[0, 1, 0]}, {GrayLevel[0], GrayLevel...

Is there a way to do conditional matrix loop using 'continue'

I have the following: n = 3; m = 5; ww = RandomReal[{0, 0.1}, {n, n}]; uu = RandomReal[{0, 1}, {m, n}]; pp = RandomReal[{0, 1}, {n, n}]; ss = RandomInteger[{0, 5}, {m, n}]; Grid[{{"ww", "uu", "pp", "ss"}, {ww // TableForm, uu // TableForm, pp // TableForm, ss // TableForm}}, Spacings -> {5, 2}, Dividers -> All] where I would like to look at every element of matrix ss and produce a matrix tt , with zeroes at the locations in ss which have zeroes, and in all other positions do the following: tt = (-1/Subscript[ww, m]) Log[(1 - uu)/(Subscript[pp, m - 1])], where Subscript[ww, m] is the value at index of ww matrix and where Subscript[pp, m - 1] is the value at index-1 of pp matrix. So for example if the first value ever read from matrix ss happens to be 2, then value taken from matrix ww would be from the row 2, but from pp would be from row 1. Also how to tell difference between a 0 as a valid value from within the matrix elemen...