Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 960
Mr.Wizard Avatar asked Apr 21 '11 17:04

Mr.Wizard


1 Answers

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

like image 86
WReach Avatar answered Nov 11 '22 16:11

WReach