用于MapAll(// @)

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)

eval2扩展

后记

在Mathematica中,总有很多方法可以达到效果.对于这个玩具评估者来说,前面的所有讨论都是过度的:

In[13]:=
         $expr /. {p -> Plus, m -> Subtract}

Out[13]= 78
Run Code Online (Sandbox Code Playgroud)

...如果只是总是那么容易检查评估者是否给出了正确的结果:)


Leo*_*rin 9

我使用它几次来做一个惰性的代码表示,它可以执行,你想要转换或解构而不需要任何评估.这是一个例子:

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.