0

If we have a graphics object,

plt = Plot[Sin[x], {x, 0, Pi}]

and we need to retrieve the discrete point data from it. This can be easily done by using Extract and Position,

First@Extract[plt, Most@First@Position[plt, Line]]

Or using Cases,

First@Cases[plt, Line[data_] -> data, Infinity]

However, my question is, might this be done via generic pattern matching? For example, some code like

plt /. (patterns-for-any-nested-or-parallel-heads) __ [ ___, Line[data_], ___] :> data

It might not be difficult to handle the parallel heads, but I do not see a obvious way to deal with the nested heads in a generic manner. Of course we can manually type all the nested heads, but this is rather specific. I am exploring the capacity of pattern matching in Mathematica. Is it possible to figure a general way to make Mathematica does it by itself? Actually I think this might be part of the algorithm under the hood of Cases.

m_goldberg
  • 107,779
  • 16
  • 103
  • 257
saturasl
  • 1,429
  • 7
  • 18
  • Could you give simple examples of a nested case and a parallel case? And what exactly we would be trying to find in those examples? – mfvonh May 27 '14 at 19:29
  • The question is, do you want to get the points via ReplaceAll or do you want to create the pattern that will fulfill the MatchQ with the Graphics but where only Line is explicitely used. – Kuba May 27 '14 at 19:30
  • For the first step, we can just try to pick out the discrete point data from plt, using pattern matching. – saturasl May 27 '14 at 19:31
  • @saturasl So in other words, how can we do the exact same thing Cases is doing without using Cases but instead using /.? – mfvonh May 27 '14 at 19:34
  • 3
    plt //. _[___, x_Line, ___] :> x – Kuba May 27 '14 at 19:35
  • @mfvonh, Yes, this could be enlightening. – saturasl May 27 '14 at 19:36
  • @Kuba, thanks, your code is a third way to get the points. But it highly exploits the built-in functions of Mathematica, like Cases. I am looking for a way to do this using simple pattern matching only with ReplaceAll. – saturasl May 27 '14 at 19:40
  • @Jacob Akkerboom, Thank you in advance. – saturasl May 27 '14 at 19:46
  • 1
    @saturasl @Kuba's way is how I would do it with patterns, but I understand what you're asking. I don't know how to achieve that with /. (I'll mull it over.) But concerning your original question, I don't think that is how Cases is implemented. My guess is the algorithm looks more like Reap[Scan[If[MatchQ[#, _Line], Sow[#]] &, plt, \[Infinity]]]. – mfvonh May 27 '14 at 19:58
  • @mfvonh, Any insight is welcome, because I am trying to get sharper understanding of Mathematica... – saturasl May 27 '14 at 20:13
  • 6
    It is appropriate to point out an important difference between Cases and Replace(All) here. The former walks the expression tree from inside out (leaves first, root last), while ReplaceAll goes the other way: outside first, then the leaves of the tree last. Yes, one of them can emulate the other, but this difference will always persist. *Also: Your question implies that ReplaceAll is somehow more fundamental ("generic pattern matching") than Cases. I wouldn't say this is the case and I'm pretty sure internally one isn't implemented in terms of the other. – Szabolcs May 27 '14 at 20:44
  • Reference: http://stackoverflow.com/q/8700934/695132 – Szabolcs May 27 '14 at 20:44
  • @Szabolcs can Map, Scan etc. be directed to walk depth-first? – mfvonh May 27 '14 at 22:16
  • @mfvonh I don't think it can be controlled in what order they traverse the expression, but you can always use level specifications to restrict them to work only on certain levels, and you can do multiple calls, e.g. Do[Scan[Print, expr, {i}], {i, 0, Depth[expr]}]. – Szabolcs May 27 '14 at 22:22
  • 1
    The most effective method is to use Cases which is what I use. A Graphics object describes a state machine, and if you want something that captures the structure, use a parser. But, usually that is overkill, so stick with Cases. – rcollyer May 28 '14 at 00:41
  • @Szabolcs, I intuitively agree with you about your insight on Cases and ReplaceAll. Although they all follow the standard evaluation procedure of Mathematica - each expression is evaluated until no further definitions apply - they may be controlled differently by kernel. It is a pity that there is little talk about the internal algorithm. I believe that my question can be solved by using a similiar code like the built-in code for Trace which has very amazing capabilities with various options, like TraceAbove and TraceOriginal. – saturasl May 28 '14 at 03:11
  • @rcollyer, From the result point of view, your parser partly resembles the combination of Extract and Position. I like it, because it is a beautiful attack to reveal the internal of Mathematica, to some extent. This is very useful to me, thank you. – saturasl May 28 '14 at 03:22
  • 1
    Related to Szabolcs's comment, and possibly of interest: (9209), (9233) – Mr.Wizard May 28 '14 at 06:44
  • 1

1 Answers1

4

Other than the fact that ReplaceAll and Cases traverse expressions differently, as mentioned by Szabolcs in a comment (see (9209), (9233)), you could use Throw and Catch:

r1 = First@Cases[plt, Line[data_] :> data, Infinity];

r2 = plt /. Line[data_] :> Throw[data] // Catch;

r1 === r2
True

For multiple matches you could use Sow and Reap, along with RuleCondition to handle held expressions, as in:

plt2 = Plot[{Sin[x], Cos[x], Sinc[x]}, {x, 0, 5}];

Short /@
 Reap[
   plt2 /. Line[data_] :> RuleCondition @ Sow @ data;
 ][[2, 1]]
{{{1.02041*10^-7,1.02041*10^-7},<<405>>,{5.,-0.958924}},
 {{1.02041*10^-7,1.},<<335>>,{5.,0.283662}},
 {{1.02041*10^-7,1.},<<325>>,{5.,-0.191785}}}

If for some reason you don't want to use Throw, Catch, Sow, or Reap you could manually accumulate these expressions, but there is little point in such an exercise as far as I can see. (Or you could use the lower-level StuffBag if that interests you.)

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • Concise and enlightening, you have my gratitude! – saturasl May 28 '14 at 18:13
  • Throw, Catch, Sow, and Reap can be used almost anywhere in any code, such powerful attribute might also be heavily used in the built-in code of Trace, and I think they are at very fundamental level of Mathematica language. – saturasl May 28 '14 at 18:24