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.

How can I create a function which works like Block but takes a list of rules as its first argument, rather than a list of assignments?

The function needs to be able to work with rules supplied either as an explicit list or via a symbol, and it should work when some of the symbols to be blocked have global values.

Test code:

a = 100;
code := a + b;
rules := {a -> 1, b -> 2};

(* using an explicit list of rules *)
ruleBlock[{a -> 1, b -> 2}, code]
(* 3 *)

(* using a symbol *)
ruleBlock[rules, code]
(* 3 *)

(* global values are unaffected *)
{a, b}
(* {100, b} *)

I have a working solution (below) but I have had to write separate functions for List and Symbol patterns, and I find the code a bit cumbersome. I would like to know if there is a simpler, cleaner way to write ruleBlock.

SetAttributes[ruleBlock, HoldAll]

ruleBlock[rules_List, code_] := 
 Replace[Apply[Set, Hold[rules], {2}], Hold[x_] :> Block[x, code]]

ruleBlock[rules_Symbol, code_] := 
 Replace[Apply[Set, OwnValues[rules] /. HoldPattern[{_ :> r_}] :> Hold[r], {2}], 
  Hold[x_] :> Block[x, code]]
share|improve this question
    
You've got a bit of a problem in the case rules assigned to a symbol if the LHS of some of those rules already has a global value, as they will evaluate. How do you want to handle that? –  Mr.Wizard Sep 5 '13 at 13:07
    
Oh, I see you're digging out OwnValues. I forget who I'm talking to as most people wouldn't think to use := to hold a list of values like this, but I would. :-) –  Mr.Wizard Sep 5 '13 at 13:10
3  
@Mr.Wizard, the rule list might also be defined with = before the global values are assigned. Ideally there wouldn't be any global values for the LHS of the rules, but I am working with a large body of code written by an incompetent fool (i.e. myself, several years ago...) –  Simon Woods Sep 5 '13 at 13:24
    
You made my profile quotes. :-) –  Mr.Wizard Sep 5 '13 at 13:26

1 Answer 1

up vote 3 down vote accepted

The only alternative that comes to mind is to use my step function to extract a definition rather than digging around in *Values lists; this has the advantage of being more robust as it will work with other kinds of definitions as well.

SetAttributes[ruleBlock, HoldAll]

ruleBlock[L_List, body_] := Block @@ Join[Apply[Set, Hold[L], {2}], Hold[body]]

ruleBlock[obj_, body_] := step[obj] /. _[x_] :> ruleBlock[x, body]

An extension you may wish to consider is handling for the undocumented := syntax of Block which holds the RHS of assignments. This adds considerable code length but also flexibility. As above but with:

ruleBlock[L_List, body_] := Block @@ Join[
    Replace[Hold[L], {(a_ -> b_) :> (a = b), (a_ :> b_) :> (a := b)}, {2}],
    Hold[body]
  ]
share|improve this answer
    
This is great, thanks. Much more readable than mine, and as you say, more robust. I love the Block @@ Join[Hold[x], Hold[y]] construction - I always forget that Join works with any head. –  Simon Woods Sep 5 '13 at 14:24
    
@Simon You're welcome. I wasn't sure if it would be useful to you. I think I finally figured out what I was trying to do re: :=. One remaining issue: ruleBlock["null", code] returns "null" because "null" is inert. If you want the original expression returned you'll have to add a Condition. –  Mr.Wizard Sep 5 '13 at 14:37
    
Oh it's definitely useful. Another nice feature, I've just realised, is that if I have a chain of definitions like r1:={a->1}; r2:=r1; r3:=r2 I can use r3 and your code will recursively call ruleBlock until the symbol resolves to a list. –  Simon Woods Sep 5 '13 at 15:39
    
@Simon Yes, I should have explicitly stated that. I used it for the that reason in (19900). –  Mr.Wizard Sep 5 '13 at 16:02

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.