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]]
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
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 :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With