免责声明:虽然我的回复提供了解决问题的方法,但我不建议将其用于常规使用。我提供它是因为它可能具有一些学术兴趣。
有时,通常在调试上下文中,我渴望地查看 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 和一些启发式方法来猜测“单级评估”何时完成,然后使用Throw 和Catch 提前终止评估序列。
对于“第一级评估”保持在标准评估范围内的函数定义,启发式方法似乎可以令人满意地工作。对于那些不这样做的人来说,它也惨遭失败。我也确信它会与某些评估属性的应用混淆。
尽管存在这些错误,但在尝试调试甚至只是理解具有大量标准模式匹配定义的函数时,我仍然觉得这个函数很方便。