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]]
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$=
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$