9

Why does Cases[] output an element only in the first of the following lines?

Cases[{1, a -> 2 b}, HoldPattern[a -> 2 b]]
(*{a->2b}*)
Cases[{1, a -> 1/2 b}, HoldPattern[a -> 1/2 b]] 
(*{}*)
Cases[{1, a -> π b}, HoldPattern[a -> π b]] 
(*{}*)
Cases[{1, a -> c b}, HoldPattern[a -> c b]] 
(*{}*)
kglr
  • 394,356
  • 18
  • 477
  • 896
Giancarlo
  • 712
  • 4
  • 11
  • 3
    compare FullForm@HoldPattern[a -> 1/2 b] and FullForm@{1, a -> 1/2 b}, the rest is about reordering. – Kuba Aug 23 '18 at 08:38
  • Huh, that was a nasty one! – Henrik Schumacher Aug 23 '18 at 08:58
  • 2
    Use PatternSequence in place of HoldPattern – kglr Aug 23 '18 at 08:59
  • @kglr I think that is worth an answer and an explanation. (I'm personally interested.) – Henrik Schumacher Aug 23 '18 at 09:01
  • 1
    See also this: https://mathematica.stackexchange.com/a/73020/5478 – Kuba Aug 23 '18 at 09:12
  • @HenrikSchumacher, the explanation is in Kuba's comment and Giancarlo's answer; that is, HoldPattern is HoldAll, hence does not evaluate its argument, whereas PatternSequence does evaluate its argument. – kglr Aug 23 '18 at 09:19
  • @kglr: Wow, an interesting hack! Just like HoldPattern, the head PatternSequence shields Rule from being interpreted by Cases, but does not have the HoldAll attribute, allowing the pattern to evaluate first! Wondering if there are subtleties tho. – kkm -still wary of SE promises Aug 23 '18 at 09:28
  • @kglr have not thought about this, nice, better: Cases[{1/b -> 2}, 1/b -> 2 // PatternSequence] than Cases[{1/b -> 2}, Verbatim[Rule][1/b , 2]] – Kuba Aug 23 '18 at 09:30

3 Answers3

8

As an alternative to HoldPattern[Evaluate[...] you can use PatternSequence which evaluates its argument:

{Cases[{1, a -> 2 b}, PatternSequence[a -> 2 b]],
 Cases[{1, a -> 1/2 b}, PatternSequence[a -> 1/2 b]],
 Cases[{1, a -> π b}, PatternSequence[a -> π b]],
 Cases[{1, a -> c b}, PatternSequence[a -> c b]]}

{{a -> 2 b}, {a -> b/2}, {a -> b π}, {a -> b c}}

Alternatively, give the pattern a name:

{Cases[{1, a -> 2 b}, p : (a -> 2 b)],
 Cases[{1, a -> 1/2 b}, p : (a -> 1/2 b)],
 Cases[{1, a -> π b}, p : (a -> π b)],
 Cases[{1, a -> c b}, p : (a -> c b)]}

{{a -> 2 b}, {a -> b/2}, {a -> b π}, {a -> b c}}

kglr
  • 394,356
  • 18
  • 477
  • 896
5

Thanks to Kuba's comment now I see: HoldPattern prevents the evaluation of its argument, so Cases don't match the element in the list a->b/2 that is

Times[Rational[1, 2], b]

with the unevaluated pattern

Times[b, Power[2, -1]]

Evaluate the argument of HoldPattern solves the problem:

Cases[{1, a -> b/2}, HoldPattern[Evaluate[a -> b/2]]]
(*{a -> b/2}*)

Thanks Kuba!

Giancarlo
  • 712
  • 4
  • 11
  • You can define myHoldPattern[x___] = HoldPattern[x]; and use in place of HoldPattern to reduce clutter. Since myHoldPattern does not hold its argument, unlike the bona fide HoldPattern, the evaluation will have happened by the time x is wrapped into HoldPattern for matching. – kkm -still wary of SE promises Aug 23 '18 at 09:19
2

Here is another way by using Verbatim. Maybe it feels a bit less hacky.

{
 Cases[{1, a -> 2 b}, Verbatim[a -> 2 b]],
 Cases[{1, a -> 1/2 b}, Verbatim[a -> 1/2 b]],
 Cases[{1, a -> π b}, Verbatim[a -> π b]],
 Cases[{1, a -> c b}, Verbatim[a -> c b]]
 }

{{a -> 2 b}, {a -> b/2}, {a -> b π}, {a -> b c}}

Henrik Schumacher
  • 106,770
  • 7
  • 179
  • 309