开发者

How to Block Symbols without evaluating them?

开发者 https://www.devze.com 2023-03-10 03:27 出处:网络
Suppose I have a list of names of Symbols: f1 := Print[\"f1 is evaluated!\"]; list = {\"f1\", \"f2\"}; The obvious way to Block these Symbols leads to evaluation of them:

Suppose I have a list of names of Symbols:

f1 := Print["f1 is evaluated!"];
list = {"f1", "f2"};

The obvious way to Block these Symbols leads to evaluation of them:

In[19]:= With[{list=Symbol/@list},Block[list,f1//ToString]]
During evalua开发者_StackOverflow社区tion of In[19]:= f1 is evaluated!
During evaluation of In[19]:= f1 is evaluated!
Out[19]= Null

But without evaluation we could Block them without any problem:

In[20]:= Block[{f1, f2}, f1 // ToString]
Out[20]= "f1"

Is it possible to inject this list into the Block scope without evaluating the Symbols?


Here is yet another technique to do this:

SetAttributes[blockAlt,HoldRest];
blockAlt[s : {__String}, body_] :=
   Replace[Join @@ ToHeldExpression[s], Hold[x__] :> Block[{x}, body]]

We save here on pure functions, due to the disruptive nature of rules (they don't respect other scoping constructs, including themselves)

EDIT

Yet another alternative (even shorter):

SetAttributes[blockAlt1, HoldRest];
blockAlt1[s : {__String}, body_] :=
   Block @@ Append[ToHeldExpression@ToString[s], Unevaluated[body]] 


Disclaimer: While my response provides a solution to the problem as expressed, I do not recommend it for regular use. I offer it up because it may be of some academic interest.

From time-to-time, usually in a debugging context, I have looked longingly at Lisp's MACROEXPAND-1 and wished for a Mathematica function which applies only one level of evaluation to its argument(s). Let's call this mythical function EvaluateOnce. It would find the transformation rule applicable to the expression and apply only that rule, something like this:

In[19]:= fact[0] = 1; fact[x_] := x * fact[x - 1]
         EvaluateOnce[fact[5]]
Out[19]= Hold[5 fact[5-1]]

In[20]:= f1 := Print["f1 is evaluated!"];
         EvaluateOnce[Symbol["f1"]]
Out[20]= Hold[f1]

It would work on multiple expressions as well:

In[21]:= EvaluateOnce[1 + 2 * 3, Sqrt @ Sin @ Pi]
Out[22]= Hold[1+6, Sqrt[0]]

The current question could benefit from such a capability for then the solution could be expressed as:

EvaluateOnce @@ Symbol /@ Hold @@ list /.
  Hold[args__] :> Block[{args}, f1 // ToString]

Alas, there are a number of technical obstacles to writing such a function -- not least of which is a certain amount of fuzziness about what exactly constitutes a "single level of evaluation" in Mathematica. But fools rush in where angels fear to tread, so I offer this hack:

ClearAll@EvaluateOnce
SetAttributes[EvaluateOnce, HoldAllComplete]
EvaluateOnce[exprs:PatternSequence[_, __]] :=
  Replace[Hold @@ Evaluate /@ EvaluateOnce /@ Hold[exprs], Hold[e_] :> e, 1]
EvaluateOnce[expr_] :=
  Module[{depth = 0, length = 1+Length@Unevaluated@expr, tag, enter, exit}
  , SetAttributes[exit, HoldAllComplete]
  ; enter[in_]:= If[1 === depth && 0 === length, Throw[in, tag], ++depth]
  ; exit[in_, out_] := (If[2 === depth, length--]; depth--)
  ; Hold @@ Catch[With[{r = TraceScan[enter, expr, _, exit]}, Hold[r]], tag]
  ]

This function comes without a warranty :) It uses TraceScan and some heuristics to guess when a "single level of evaluation" is complete and then uses Throw and Catch to terminate the evaluation sequence early.

The heuristics appear to work satisfactorily for function definitions whose "first level of evaluation" stays within the bounds of standard evaluation. It also fails miserably for those that don't. I'm also certain that it will get confused with the application of some evaluation attributes.

Notwithstanding these faults, I still find this function handy when trying to debug or even just understand functions with lots of standard pattern-matching definitions.


You could try to use ToExpression:

In[9]:= list = {"f1", "f2"};

In[19]:= f1 = 25;

In[20]:= ToExpression[
 StringJoin["{", Riffle[list, ","], "}"], InputForm, 
 Function[vars, Block[vars, f1], HoldAll]]

Out[20]= 25


You may consider this construct:

SetAttributes[block, HoldRest]

block[s : {__String}, body_] := 
 Function[, Block[{##}, body], HoldAll] @@ 
  Join @@ MakeExpression /@ s

Second attempt at a shorter version of Leonid's second function:

block =
  Function[, Block @@ ToHeldExpression@ToString@#~Join~Hold@#2, HoldRest]
0

精彩评论

暂无评论...
验证码 换一张
取 消