函数MapAll
被视为足够重要以保证简短形式 //@
,但我很少使用它,特别是与其他人相比,例如 /@
, /.
和 @@@
我几乎无处不在。
MapAll
? 最佳答案
//@
是“后序树遍历”。它访问树结构中的每个节点,在节点本身之前访问每个节点的子节点。提供的函数以每个节点作为其参数被调用,该节点的子节点已经被先前的调用“扩展”了。树数据结构很常见,但需要遍历它们。但我敢说 //@
的主要用例在 Mathematica 上下文中是实现评估器。
让我们从创建一个随机的树结构表达式开始:
In[1]:=
$expr = 500 //.
n_Integer /; RandomInteger[100] < n :>
RandomChoice[{p, m}] @@ RandomInteger[Floor[n/2], 2]
$expr//TreeForm
Out[2]= p[m[p[34, 22], m[11, 24]], p[m[6, 7], 10]]
假设我们想使用这种形式的表达式为迷你语言创建一个评估器,其中
p
表示“加”和 m
表示减。我们可以为这种迷你语言编写一个递归下降的评估器,因此:In[4]:=
eval1[p[a_, b_]] := eval1[a] + eval1[b]
eval1[m[a_, b_]] := eval1[a] - eval1[b]
eval1[a_] := a
In[7]:=
eval1[$expr]
Out[7]= 78
必须明确地将递归调用写入
eval1
会让人厌烦。在每个规则中。此外,很容易忘记在规则中添加递归调用。现在考虑相同评估器的以下版本:In[8]:=
eval2[p[a_, b_]] := a + b
eval2[m[a_, b_]] := a - b
eval2[a_] := a
递归调用的“噪音”已被删除,以便规则更易于阅读。我们当然可以找到某种方法来自动插入必要的递归调用吗?输入
//@
:In[11]:=
eval2 //@ $expr
Out[11]= 78
它正是我们所需要的。稍微滥用从函数式编程中借来的术语,我们可以说我们已经解除了
eval2
成为递归下降函数。您可以在下图中看到效果。In[12]:=
"eval2" //@ $expr // TreeForm
后记
在 Mathematica 中,总是有很多方法可以实现效果。对于这个玩具评估器,前面的所有讨论都是多余的:
In[13]:=
$expr /. {p -> Plus, m -> Subtract}
Out[13]= 78
...如果只有它总是那么容易检查评估者是否给出正确的结果:)
关于wolfram-mathematica - 用于 MapAll (//@ ),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5747742/