6
$\begingroup$

Consider this list of rules

rules = {a -> 1, b -> 2, c -> 3}

I'd like to fish some of the rules out of such a list using Cases. All is well if I hard code the Alternatives to fish out:

Cases[rules, HoldPattern[a|b -> _]]

{a -> 1, b -> 2}

But, if I try to dynamically generate the Alternatives from data, it fails:

targets = {a, b}
Cases[rules, HoldPattern[Alternatives @@ targets -> _]]

{}

I've tried the following shots-in-the-dark

Cases[rules, HoldPattern[Evaluate[Alternatives @@ targets] -> _]]

{}

Cases[rules, HoldPattern[ReleaseHold[Alternatives @@ targets] -> _]]

{}

MMA 10.0.2.0 on Mac Mavericks.

$\endgroup$
5
$\begingroup$

HoldPattern has attribute HoldAll

Attributes[HoldPattern]
(* {HoldAll, Protected} *)

So one can use the following injection

Cases[rules, HoldPattern[Alternatives@## -> _] & @@ targets]
(* {a -> 1, b -> 2} *)

P.S. Conceptually, HoldPattern is here the wrong tool for the job, even if this works. See Leonid's answer.

$\endgroup$
  • $\begingroup$ Nice reminder of the general utility of thunking (wrapping an expression in a function). $\endgroup$ – Reb.Cabin Feb 1 '15 at 18:03
  • $\begingroup$ Note that my original expression is equivalent to Cases[rules, HoldPattern[Alternatives@@## -> _]& @ targets, so the deeper lesson is about the distinction between Apply, @@, and ordinary application @. $\endgroup$ – Reb.Cabin Feb 1 '15 at 18:09
6
$\begingroup$

The issues with your code

There are two different issues here. First is that indeed, HoldPattern would prevent Alternatives@@... to evaluate. This is why

Cases[rules, HoldPattern[Alternatives @@ targets -> _]]

will not work. However, when you use

Cases[rules, HoldPattern[Evaluate[Alternatives @@ targets]] -> _]

you run into another issue: Cases has an extended syntax Cases[expr, lhs_->rhs_,...], where it interprets Rule as a part of its extended syntax, rather than a part of expression to be matched. Conceptually, the correct solution here would be, for example, to use Verbatim to escape Rule:

Cases[rules,Verbatim[Rule][HoldPattern[Evaluate[Alternatives @@ targets]], _]]

(* {a -> 1, b -> 2} *)

HoldPattern and Verbatim

However, I have to note that the whole business with HoldPattern here seems either unnecessary or insufficient. If you don't worry about possible evaluation of a, b, etc, then you don't need HoldPattern:

Cases[rules, Verbatim[Rule][Alternatives @@ targets, _]]

(* {a -> 1, b -> 2} *)

So, it looks like a classic case of misuse of HoldPattern, where you use HoldPattern where Verbatim should be used.

OTOH, if you do worry about evaluations of a, b, etc, then you need to do a whole lot more to prevent their evaluation, than just in the line with Cases. But this doesn't seem to be the case you are interested in.

$\endgroup$
  • $\begingroup$ The documentation (under "Cases" -> "Possible Issues") sets the trap by recommending HoldPattern when matching rules in Cases. $\endgroup$ – Reb.Cabin Feb 1 '15 at 18:14
  • 1
    $\begingroup$ @Reb.Cabin Then the documentation isn't quite right :) This is not the only place where docs miss the point. I will eventually make a suggestion report to change this. $\endgroup$ – Leonid Shifrin Feb 1 '15 at 18:15
  • $\begingroup$ @Reb.Cabin Historically, there were times when both HoldPattern and Verbatim were the same, named Literal (which had, essentially, the functionality of HoldPattern rather than Verbatim). It was precisely the realization that the role of pattern escaping is different from the role of pattern-transparent holding wrapper (preventing pattern evaluation), that led to the introduction of HoldPattern and Verbatim, instead of Literal. $\endgroup$ – Leonid Shifrin Feb 1 '15 at 18:18
  • $\begingroup$ I also notice that you wrote HoldPattern[Evaluate[Alternatives @@ targets]] -> _, where my (guessworky) attempt was HoldPattern[Evaluate[Alternatives @@ targets] -> _] because I did appreciate the extended syntax of Cases and tried to wrap my HoldPattern around the whole thing. Still doesn't work... I have some more learning to do about Verbatim versus HoldPattern. $\endgroup$ – Reb.Cabin Feb 1 '15 at 18:19
  • $\begingroup$ @Reb.Cabin Well, your code was missing ending brackets, so there were a number of ways to interpret what you had in mind. $\endgroup$ – Leonid Shifrin Feb 1 '15 at 18:20
2
$\begingroup$

This should probably be a comment (and maybe I have missed it):

This does not address the issue re: Alternatives but note FilterRules does this.

rules = {a -> 1, b -> 2, c -> 3};
FilterRules[rules, {a, b}]

or

rules = {a -> 1, b -> 2, c -> 3, d -> 4}
FilterRules[rules, {d, a, b}]

yields: {a -> 1, b -> 2, d -> 4}

$\endgroup$
2
$\begingroup$

All you need to do is to keep Cases from seeing the second argument as a rule. Adding a Pattern:

Cases[rules, x : (Alternatives @@ targets -> _)]
{a -> 1, b -> 2}

Not nearly as clean as FilterRules but a bit more general.

$\endgroup$
2
$\begingroup$

An alternative is to use PatternSequence which does evaluate its argument:

Cases[rules, PatternSequence[Alternatives @@ targets -> _]]

{a -> 1, b -> 2}

$\endgroup$
1
$\begingroup$

This question already has several answers, but I'd like to comment on why the OPs initial attempt to use Evaluate failed (even though they were so close to victory). The following attempt was made for the matching pattern:

HoldPattern[Evaluate[Alternatives @@ targets] -> _]

The problem here is that Evaluate simply will not do anything. Look at the FullForm of this expression:

HoldPattern[Rule[Evaluate[Apply[Alternatives, targets]], Blank[]]]

As you can see, Evaluate is still there, meaning that it didn't... Well, evaluate. This is a common misunderstanding about how Evaluate works: it will only override a HoldAll attribute if it appears as a head directly one level down inside of a function that has a hold attribute. In this case, Evaluate appears inside of Rule, where it will do nothing (and can't, since rule doesn't have any hold attributes).

To clarify the point, try evaluating the following two expressions:

Hold[Evaluate[1 + 1]]
(*Hold[2]*)
Hold[{Evaluate[1 + 1]}]
(*Hold[{Evaluate[1 + 1]}]*)

In short, Evaluate cannot work at a deeper level of a held function.

Going back to the original question, we can now understand why the original attempt didn't work, but this one does:

targets = {a, b};
Cases[
  {a -> 1, b -> 2, c -> 3}, 
  HoldPattern[Evaluate[Alternatives @@ targets -> _]]
]

Out[15]= {a -> 1, b -> 2}
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.