8
$\begingroup$

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]]
$\endgroup$
4
  • $\begingroup$ 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? $\endgroup$
    – Mr.Wizard
    Commented Sep 5, 2013 at 13:07
  • $\begingroup$ 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. :-) $\endgroup$
    – Mr.Wizard
    Commented Sep 5, 2013 at 13:10
  • 3
    $\begingroup$ @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...) $\endgroup$ Commented Sep 5, 2013 at 13:24
  • $\begingroup$ You made my profile quotes. :-) $\endgroup$
    – Mr.Wizard
    Commented Sep 5, 2013 at 13:26

1 Answer 1

6
$\begingroup$

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]
  ]
$\endgroup$
4
  • $\begingroup$ 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. $\endgroup$ Commented Sep 5, 2013 at 14:24
  • $\begingroup$ @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. $\endgroup$
    – Mr.Wizard
    Commented Sep 5, 2013 at 14:37
  • $\begingroup$ 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. $\endgroup$ Commented Sep 5, 2013 at 15:39
  • $\begingroup$ @Simon Yes, I should have explicitly stated that. I used it for the that reason in (19900). $\endgroup$
    – Mr.Wizard
    Commented Sep 5, 2013 at 16:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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