Mr.*_*ard 10 wolfram-mathematica
该功能MapAll被看作是重要的,足以保证短表//@,但我很少使用它,尤其是相对于其他人一样/@,/.和@@@我用几乎无处不在.
什么样的应用最好地利用MapAll?
它主要用于某些领域或编程风格吗?
与其他运营商相比,它可以多久使用一次?
WRe*_*ach 13
//@是一个"后序树遍历".它访问树结构中的每个节点,每个节点的子节点在节点本身之前被访问.调用提供的函数,每个节点作为其参数,节点的子节点已经被前一个调用"扩展".树数据结构很常见,需要遍历它们.但我敢说,//@在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]]
Run Code Online (Sandbox Code Playgroud)

假设我们想要使用这种形式的表达式为迷你语言创建一个评估器,其中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
Run Code Online (Sandbox Code Playgroud)
eval1在每个规则中必须显式地写入递归调用是令人厌烦的.此外,很容易忘记在规则中添加递归调用.现在考虑同一评估者的以下版本:
In[8]:=
eval2[p[a_, b_]] := a + b
eval2[m[a_, b_]] := a - b
eval2[a_] := a
Run Code Online (Sandbox Code Playgroud)
已删除递归调用的"噪音",以便更容易阅读规则.当然,我们可以找到一些方法来自动插入必要的递归调用?输入//@:
In[11]:=
eval2 //@ $expr
Out[11]= 78
Run Code Online (Sandbox Code Playgroud)
它正是我们所需要的.稍微滥用从函数式编程中借用的术语,我们可以说我们已经提升eval2为递归下降函数.您可以在下图中看到效果.
In[12]:=
"eval2" //@ $expr // TreeForm
Run Code Online (Sandbox Code Playgroud)

后记
在Mathematica中,总有很多方法可以达到效果.对于这个玩具评估者来说,前面的所有讨论都是过度的:
In[13]:=
$expr /. {p -> Plus, m -> Subtract}
Out[13]= 78
Run Code Online (Sandbox Code Playgroud)
...如果只是总是那么容易检查评估者是否给出了正确的结果:)
我使用它几次来做一个惰性的代码表示,它可以执行,你想要转换或解构而不需要任何评估.这是一个例子:
ClearAll[myHold, makeInertCode];
SetAttributes[{myHold, makeInertCode}, HoldAll];
makeInertCode[code_] :=
MapAll[myHold, Unevaluated[code], Heads -> True]
Run Code Online (Sandbox Code Playgroud)
这是一个例子:
In[27]:=
icd = makeInertCode[
With[{x = RandomInteger[{1, 10}, 20]},
Extract[x, Position[x, _?OddQ]]]
]
Out[27]= myHold[myHold[With][myHold[myHold[List][myHold[myHold[Set][myHold[x],
myHold[myHold[RandomInteger][myHold[myHold[List][myHold[1],myHold[10]]],myHold[20]]]]]]],
myHold[myHold[Extract][myHold[x], myHold[myHold[Position][myHold[x], myHold[myHold[
PatternTest][myHold[myHold[Blank][]], myHold[OddQ]]]]]]]]]
Run Code Online (Sandbox Code Playgroud)
现在我们可以使用标准的解构工具而不会有过早的代码评估的危险(完全myHold可以肯定,可以给出HoldAllComplete而不是HoldAll属性):
In[28]:= Cases[icd, myHold[Extract][___], Infinity]
Out[28]= {myHold[Extract][myHold[x],
myHold[myHold[Position][myHold[x],
myHold[myHold[PatternTest][myHold[myHold[Blank][]],
myHold[OddQ]]]]]]}
Run Code Online (Sandbox Code Playgroud)
一旦代码被转换/解构,它就可以被包装,Hold或者HoldComplete然后myHold可以通过例如myHold[x___]:>x重复应用的规则来移除包装器.但一般来说,MapAll的附加值在我看来相当有限,因为特别是,Map水平规范{0,Infinity}等同于它.我不认为它经常被使用.
小智 7
我不使用它,但对Listable函数有一些有趣的行为.例如:
如果你希望列表中的每个元素都有一个函数应用于它多次,具体取决于它在列表中的嵌套深度,我想这是我见过它的唯一时间.
SetAttributes[f, Listable]
(f //@ {{a}, {{b}}, c}) // Flatten
Run Code Online (Sandbox Code Playgroud)
{f[f[f[a]]], f[f[f[f[b]]]], f[f[c]]}
一般来说,虽然我想你可以在任何时候使用ReplaceAll.
| 归档时间: |
|
| 查看次数: |
600 次 |
| 最近记录: |