5

Given

expr = f[x, g[y], z]

In the following query, the pattern h_[__, c_] appears as last slot in Alternatives:

Cases[expr, (h_[c_] | h_[c_, __] | h_[__, c_, __] | h_[__, c_]) :> 
  h -> c, {0, Infinity}]

Gives

{g -> y, f -> x}

Ie, "f[__,z]" is not matched but is matched when the pattern is rotated to the first slot:

Cases[expr, (h_[__, c_] | h_[c_] | h_[c_, __] | h_[__, c_, __]) :> 
  h -> c, {0, Infinity}]

Which gives:

{g -> y, f -> z}

Souldn't Alternatives be commutative? Apparentely only the first match per level is returned. Is there a method to return x, y and z containing patterns?

alancalvitti
  • 15,143
  • 3
  • 27
  • 92

2 Answers2

8
  • Patterns in Alternatives are tried in order
  • Only the first pattern that matches is "applied" to the expression.
  • Cases does not support multiple patterns outside of Alternatives.

I suppose it could be interesting to debate that design decision but nevertheless that's the way it works at this time.

You could of course search with multiple passes:

expr = f[x, g[y], z]
pat = h_[c_] | h_[c_, __] | h_[__, c_, __] | h_[__, c_];

Join @@ (Cases[expr, # :> h -> c, {0, -1}] & /@ List @@ pat)
{g -> y, f -> x, f -> g[y], f -> z}

Or using ReplaceList and Level:

rules = # :> h -> c & /@ List @@ pat
Join @@ (ReplaceList[#, rules] & /@ Level[expr, {0, -1}])

Since neither of these is efficient you could subvert the normal evaluation by using side-effects, e.g. with Condition:

Module[{f},
  f[pat] := 1 /; Sow[h -> c];
  Reap[Scan[f, expr, {0, -1}]][[2, 1]]
]
{g -> y, f -> x, f -> g[y], f -> z}

Or more cleanly, though perhaps rather enigmatically, using Cases itself:

Reap[Cases[expr, pat :> 1 /; Sow[h -> c], {0, -1}];][[2, 1]]
{g -> y, f -> x, f -> g[y], f -> z}

Finally, if traversal order is irrelevant:

Reap[expr /. pat :> 1 /; Sow[h -> c]][[2, 1]]
{f -> x, f -> g[y], f -> z, g -> y}

A note regarding another ramification of Alternatives is here:


NOTE: It looks like my assumptions about efficiency were wrong, and the multi-pass method may be more efficient than the rest. I need to explore this further but I have neither the time nor the interest right now.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • 1
    In the last two instances I think you could simplify pat = h_[___, c_, ___], too. – mfvonh Jun 24 '14 at 23:08
  • @mfvonh Great point, but I was assuming the given patterns were merely toy examples and I did not wish to tamper with them. – Mr.Wizard Jun 24 '14 at 23:09
  • @Mr.Wizard, thanks, this is great. But how to modify so that the output only matches heads like 'f->g' rather than f->g[y]' – alancalvitti Jun 24 '14 at 23:33
  • @mfvonh, I tried h_[___, c_, ___] but it didn't work. – alancalvitti Jun 24 '14 at 23:33
  • @alancalvitti Why shouldn't f -> g[y] be included? Because g[y] is not atomic? Because g[y] has already matched a pattern? Something else? – Mr.Wizard Jun 24 '14 at 23:36
  • @Mr.Wizard, I need h -> If[AtomQ[c], c, Head[c]]. Actually a modification for a specific application that I will ask about next – alancalvitti Jun 25 '14 at 19:31
  • @Mr.Wizard, I accepted your answer b/c it works on the example I provided. However, doesn't seem to generalize, eg, applied to f[x, g[y[a, b], w, w2, w3], z] misses w2. Can you modify your approach to handle this case or should I ask a separate Q? – alancalvitti Jun 25 '14 at 20:16
  • @alancalvitti I think the input/output you desire is separate from the specific question here about Alternatives; I suggest posting a new question containing merely a clear description of the output you want for a given input, allowing responders to detach their answers from Alternatives if appropriate. (Thanks for the Accept.) – Mr.Wizard Jun 25 '14 at 22:28
2
Flatten@{Head[expr] -> # & /@ Level[expr, {1}], 
  Thread[Cases[expr, x_[_] -> x] -> Cases[expr, _[x_] -> x]]}
Basheer Algohi
  • 19,917
  • 1
  • 31
  • 78