Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

A number of functions provide for results to be wrapped in an arbitrary head. This is very important in cases where the results should not be evaluated. Take Level as an example:

expr = Hold[{2/2, g[8/4], 1/0}];

Level[expr, {-2}, Hold]
Hold[1/2, 1/4, 1/0]

Cases however provides no such parameter, causing undesired evaluation:

Cases[expr, _Times, 3]

Power::infy: Infinite expression 1/0 encountered. >>

{1, 2, ComplexInfinity}  (* failure *)

How can this behavior be attained?

share|improve this question

1 Answer 1

up vote 15 down vote accepted

The best I have is manual RHS holding and Join, after which an arbitrary head could be Applied:

Join @@ Cases[expr, x : _Times :> Hold[x], 3]
Hold[2/2, 8/4, 1/0]

This could be done automatically as follows:

makeHeld[(L_ -> R_) | (L_ :> R_)] := L :> HoldComplete[R];
makeHeld[pat_] := x : pat :> HoldComplete[x];

heldCases[expr_, rule_, args___] :=
 Join @@ Cases[Unevaluated @ expr, makeHeld @ rule, args]

I am now reasonably happy with this but I still wonder if there is a more elegant or efficient method.


For fun here is a one-line definition of makeHeld using "vanishing patterns" and Part properties:

makeHeld[(L_ -> R_) | (L_ :> R_) | L_] := x : L -> HoldComplete[R, x][[{1}]];

It's not quite as efficient and clarity suffers so I won't replace the version above with it.

share|improve this answer
    
+1. I don't know why you dislike the code, it looks pretty good to me. Just one thing - I'd use Cases[Unevaluated[expr], Replace[...],args]. –  Leonid Shifrin Sep 6 '13 at 6:59
    
@Leonid I'm glad it looks good. I guess it just seems like a lot of code for what I think is a rather obvious omission from Cases syntax. Unevaluated[expr] does make sense regarding the name holdCases but it was my intent instead to implement this as if it was a parameter of Cases, just as it is for Level, and Cases does not have HoldFirst. –  Mr.Wizard Sep 6 '13 at 7:02
    
@Leonid I changed the code somewhat; I think it's easier to read and a bit more efficient. Do you agree/confirm? –  Mr.Wizard Sep 6 '13 at 7:22
1  
@Mr.Wizard The reason I suggested Unevaluated is that otherwise the expression will evaluate, once passed to heldCases, even when you wrap it in one level of Unevaluated, because there is a rewrite step heldCases to Cases. In other words, when I need to destructure an expression while keeping it unevaluated, I use Cases[Unevaluated[expr],...], while doing the same with heldCases as heldCases[Unevaluated[expr],... won't work with your current code. Re: rewrite - quite nice, I agree. –  Leonid Shifrin Sep 6 '13 at 10:49
1  
@rcollyer x := godObject["rcollyer", "confusticate"] -- meet me in chat ;-) –  Mr.Wizard Sep 6 '13 at 12:59

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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