12

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?

Mr.Wizard
  • 24,179
  • 5
  • 44
  • 125
  • 3
    http://books.google.com/books?id=3OtUpdFiXvkC&pg=PA525&lpg=PA525&dq=mathematica+mapall&source=bl&ots=hQBU27AfXZ&sig=LI3VDlvNq4AkVIDA_IgzUJFBPaQ&hl=en&ei=vHawTYroJY-w0QHauKiiCQ&sa=X&oi=book_result&ct=result#v=onepage&q=mathematica%20mapall&f=false – Dr. belisarius Apr 21 '11 at 18:36
  • I have a hard time trying to remember whether I've used it at all... – Sjoerd C. de Vries Apr 21 '11 at 19:09
  • Thank you all for your replies. I had a hard time choosing which one to accept. – Mr.Wizard Apr 26 '11 at 03:52

4 Answers4

16

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

expression tree

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

eval2 expansion

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 :)

WReach
  • 18,098
  • 3
  • 49
  • 93
  • +1 Nice example! An alternative to writing an explicit `eval` would be to bend mma evaluator in a right direction. One technique I use for somewhat similar purposes is the "code freezing" - roughly speaking, I dynamically hide certain heads, so that they remain inert during the code generation. The result is that mma code gets generated (or, rather, expanded) by partial evaluation of the initial code. But this is more relevant for construction of compilers to mma, rather than evaluators / interpreters. – Leonid Shifrin Apr 25 '11 at 11:57
10

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.

Leonid Shifrin
  • 22,449
  • 4
  • 68
  • 100
  • @Sjoerd: one application where this is indeed quite useful is in constructing a diff of two mma expressions (which can be code). Such diff can be quite useful, more so than the standard string-based one, in the context of mma. I started with `MapAll`, but ended up using `MapIndexed` with `{0,Infinity}` for the diff however, since I needed to also know the level of each element. – Leonid Shifrin Apr 22 '11 at 10:43
  • Interesting. I have been thinking about a diff in the context of the plot annotation question (http://stackoverflow.com/q/5744117/615464). Could you do a diff between an original plot and a plot with annotation and add the diff to a revised version of the first one? In that way, this method of saving annotations would be more future-proof than mine. – Sjoerd C. de Vries Apr 22 '11 at 11:23
  • @Sjoerd The task you mention sounds ambiguous, since while I can take a diff between the two images, I don't see an un-ambiguous way to formulate the "addition" to the transformed image step. Perhaps, it can be done heuristically, but then I doubt this will be robust, unless some additional constraints are imposed on plot format. – Leonid Shifrin Apr 22 '11 at 11:32
7

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.

Mr.Wizard
  • 24,179
  • 5
  • 44
  • 125
Searke
  • 779
  • 3
  • 3
4

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]
Sasha
  • 5,935
  • 1
  • 25
  • 33
  • 3
    Today I learned that adding O[x]^4 to 1/(1 + x)^a forces a series expansion in the latter. And yet, it's there in the docs, well hidden under the 'Scope' header. – Sjoerd C. de Vries Apr 21 '11 at 19:16