如何在不评估符号的情况下阻止符号?

时间:2011-06-04 04:34:03

标签: wolfram-mathematica

假设我有一个Symbol s名称列表:

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

Block这些Symbol的显而易见的方法可以评估它们:

In[19]:= With[{list=Symbol/@list},Block[list,f1//ToString]]
During evaluation of In[19]:= f1 is evaluated!
During evaluation of In[19]:= f1 is evaluated!
Out[19]= Null

但是如果没有评估,我们可以Block毫无问题地进行评估:

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

是否可以在不评估Block的情况下将此列表注入Symbol范围?

4 个答案:

答案 0 :(得分:9)

免责声明:虽然我的回复提供了所表达问题的解决方案,但我不建议将其用于常规用途。我提供它是因为它可能具有一定的学术兴趣。

有时,通常在调试环境中,我一直渴望看到Lisp的MACROEXPAND-1,并希望Mathematica函数只对其参数应用一级评估。我们称之为神话函数EvaluateOnce。它会找到适用于表达式的转换规则,并仅应用该规则,如下所示:

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]

它也适用于多个表达式:

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

当前的问题可以从这种能力中受益,然后解决方案可以表示为:

EvaluateOnce @@ Symbol /@ Hold @@ list /.
  Hold[args__] :> Block[{args}, f1 // ToString]
唉,编写这样一个函数存在许多技术障碍 - 尤其是Mathematica中究竟构成“单一评估”的一定程度的模糊性。但是傻瓜在天使们害怕踩到的地方匆匆忙忙,所以我提供了 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]
  ]

此功能没有保修:)它使用TraceScan和一些启发式方法来猜测“单一评估级别”何时完成,然后使用ThrowCatch来终止评估序列早期。

对于“第一级评估”保持在标准评估范围内的函数定义,启发式算法似乎令人满意。对于那些不这样做的人来说,它也会悲惨地失败。我也确定它会与某些评估属性的应用混淆。

尽管存在这些错误,但在尝试调试甚至只是理解具有大量标准模式匹配定义的函数时,我仍然觉得这个函数很方便。

答案 1 :(得分:8)

这是另一种技术:

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

由于规则具有破坏性(他们不尊重其他范围构造,包括他们自己),我们在这里保存纯函数

修改

另一种选择(甚至更短):

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

答案 2 :(得分:6)

您可以尝试使用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

答案 3 :(得分:6)

你可以考虑这个结构:

SetAttributes[block, HoldRest]

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

第二次尝试较短版本的狮子座第二个功能:

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