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

Ale*_*kov 13 wolfram-mathematica

假设我有一个Symbols 的名字列表:

f1 := Print["f1 is evaluated!"];
list = {"f1", "f2"};
Run Code Online (Sandbox Code Playgroud)

以明显的方式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
Run Code Online (Sandbox Code Playgroud)

但是如果没有评估我们就可以Block毫无问题

In[20]:= Block[{f1, f2}, f1 // ToString]
Out[20]= "f1"
Run Code Online (Sandbox Code Playgroud)

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

WRe*_*ach 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]
Run Code Online (Sandbox Code Playgroud)

它也适用于多个表达式:

In[21]:= EvaluateOnce[1 + 2 * 3, Sqrt @ Sin @ Pi]
Out[22]= Hold[1+6, Sqrt[0]]
Run Code Online (Sandbox Code Playgroud)

目前的问题可以从这种能力中受益,因此解决方案可以表达为:

EvaluateOnce @@ Symbol /@ Hold @@ list /.
  Hold[args__] :> Block[{args}, f1 // ToString]
Run Code Online (Sandbox Code Playgroud)

唉,编写这样一个函数存在许多技术障碍 - 尤其是Mathematica中究竟构成"单一评估"的一定程度的模糊性.但是傻瓜在天使们害怕踩到的地方匆匆忙忙,所以我提供了这个黑客:

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]
  ]
Run Code Online (Sandbox Code Playgroud)

此功能没有保修:)它使用TraceScan和一些启发式方法猜测"单级评估"何时完成,然后使用ThrowCatch提前终止评估序列.

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

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


Leo*_*rin 8

这是另一种技术:

SetAttributes[blockAlt,HoldRest];
blockAlt[s : {__String}, body_] :=
   Replace[Join @@ ToHeldExpression[s], Hold[x__] :> Block[{x}, body]]
Run Code Online (Sandbox Code Playgroud)

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

编辑

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

SetAttributes[blockAlt1, HoldRest];
blockAlt1[s : {__String}, body_] :=
   Block @@ Append[ToHeldExpression@ToString[s], Unevaluated[body]] 
Run Code Online (Sandbox Code Playgroud)

  • +1可以重新表达`ToHeldExpression [s]`(在V3中弃用)为'ToExpression [s,InputForm,Hold]`. (4认同)

Sas*_*sha 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
Run Code Online (Sandbox Code Playgroud)


Mr.*_*ard 6

你可以考虑这个结构:

SetAttributes[block, HoldRest]

block[s : {__String}, body_] := 
 Function[, Block[{##}, body], HoldAll] @@ 
  Join @@ MakeExpression /@ s
Run Code Online (Sandbox Code Playgroud)

第二次尝试更短版Leonid的第二个功能:

block =
  Function[, Block @@ ToHeldExpression@ToString@#~Join~Hold@#2, HoldRest]
Run Code Online (Sandbox Code Playgroud)

  • 最好为`block`而不是`HoldAll`设置`HoldRest`属性.+1 (5认同)