开发者

Uses for MapAll ( //@ )

The function MapAll开发者_开发知识库 was seen as important enough to warrant the short form //@, yet I rarely use it, especially compared to others like /@, /. and @@@ which I use almost everywhere.

  • What applications best leverage MapAll?

  • Is it used mostly in certain fields or programming styles?

  • How often can it be used compared to other operators?


//@ is a "post-order tree traversal". It visits every node in a tree structure, each node's children being visited before the node itself. The supplied function is called with each node as its argument, the node's children already having been "expanded" by a previous call. Tree data structures are common, along with the need to traverse them. But I dare say that the primary use case for //@ in a Mathematica context is to implement evaluators.

Let's start by creating a random tree-structured expression:

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]]

Uses for MapAll ( //@ )

Let's say that we want to create an evaluator for a mini-language using expressions of this form, where p means "plus" and m means minus. We can write a recursive-descent evaluator for this mini-language thus:

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

It gets tiresome to have to explicitly write the recursive calls to eval1 in each of the rules. Furthermore, it is easy to forget to add the recursive call in a rule. Now consider the following version of the same evaluator:

In[8]:=
        eval2[p[a_, b_]] := a + b
        eval2[m[a_, b_]] := a - b
        eval2[a_] := a

The "noise" of the recursive calls has been removed so that the rules are easier to read. Surely we can find some way to automatically insert the necessary recursive calls? Enter //@:

In[11]:=
         eval2 //@ $expr
Out[11]= 78

It does just what we need. With slight abuse of terminology borrowed from functional programming, we can say that we have lifted eval2 to be a recursive-descent function. You can see the effect in the following diagram.

In[12]:=
         "eval2" //@ $expr // TreeForm

Uses for MapAll ( //@ )

Postscript

In Mathematica there are always many ways to achieve an effect. For this toy evaluator, all of the preceding discussion is overkill:

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

Out[13]= 78

... if only it were always so easy to check if an evaluator was giving the right results :)


I used it a few times to make an inert representation of code which may execute and which you want to transform or destructure without any evaluation. Here is an example:

ClearAll[myHold, makeInertCode];
SetAttributes[{myHold, makeInertCode}, HoldAll];
makeInertCode[code_] :=
   MapAll[myHold, Unevaluated[code], Heads -> True]

Here is an example:

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]]]]]]]]]

Now we can use the standard destructuring tools without the danger of premature code evaluation (to be totally sure, myHold can be given HoldAllComplete rather than HoldAll attribute):

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]]]]]]}

once the code is transformed / destructured, it can be wrapped in Hold or HoldComplete, and then the myHold wrappers can be removed by, for example, a rule like myHold[x___]:>x, applied repeatedly. But generally, the added value of MapAll seems to me rather limited, because, in particular, Map with the level specification {0,Infinity} is equivalent to it. I don't think it is frequently used.


I don't use it, but has some fun behavior for Listable functions. For example:

If you want each element in a list to have a function applied to it a number of times depending on its nested depth in the list, I guess this the only time I've seen it used.

SetAttributes[f, Listable]

(f //@ {{a}, {{b}}, c}) // Flatten
    {f[f[f[a]]], f[f[f[f[b]]]], f[f[c]]}

Generally though I would imagine you could use ReplaceAll whenever you would use this.


I would use it as a lazy way to apply algebraic expressions on objects algebraic functions do not work with:

In[13]:= ser = 1/(1 + x)^a + O[x]^4

Out[13]= SeriesData[x, 0, {
 1, -a, Rational[1, 2] (a + a^2), 
  Rational[1, 6] ((-2) a - 3 a^2 - a^3)}, 0, 4, 1]

In[14]:= Factor[ser]

Out[14]= SeriesData[x, 0, {
 1, -a, Rational[1, 2] (a + a^2), 
  Rational[1, 6] ((-2) a - 3 a^2 - a^3)}, 0, 4, 1]

In[15]:= MapAll[Factor, ser]

Out[15]= SeriesData[x, 0, {
 1, -a, Rational[1, 2] a (1 + a), 
  Rational[-1, 6] a (1 + a) (2 + a)}, 0, 4, 1]
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜