views:

126

answers:

3

The mathematica documentation on Evaluate at possible issues says:

Evaluate works only on the first level, directly inside a held function

Why does Mathematica have this limitation? So if I have an expression with more than one level take this simplified example:

Hold[Plus[Plus[2, 2], 2]]]

Now suppose I want to see what the answer is to the the second Plus, without evaluating anything on levels below it. I've tried different things such as:

In[290]:= Hold[Plus[Evaluate[Plus[2, 2]], 2]]
Out[290]= Hold[Evaluate[2+2]+2]

In[287]:= Hold[Plus[ReleaseHold[Hold[Plus[2, 2]]], 2]]
Out[287]= Hold[ReleaseHold[Hold[2+2]]+2]

The first Hold keeps everything unevaluated at and beyond the first level in this case. The goal is to control evaluation of an expression at each stage from the most inner nested function to the outer one using successive Hold, ReleaseHold and Evaluate functions to achieve that. I know I could use trace to see what happens beyond level one in an expression but that is different and sometimes complex to read with longer expressions.

It seems like the only way is to extract and totally dismantle the expression into lists using Extract, Part or Level; evaluate part of the expression that I want; then reconstruct and re-map the expression back together for each stage. Are there any other approaches or functions for achieving this I could consider?

Edit: This might be a better example to look at the approach of releasing the first hold. With the expression:

Hold[Plus[Plus[2, Plus[2,2]], 2]]]

If you release the first hold and place a hold on a higher level in a expression at the third Plus, to look like this:

in = Plus[Plus[2, Hold[Plus[2,2]]], 2]]]
out = Hold[2+2]+4

You find that Mathematica will evaluate lower levels in the background when you really want it to wait.

+3  A: 

I can't give the exact reason why Evaluate "works only on the first level, directly inside a held function" but I suspect it's partly efficiency, in that it would be slow if the evaluator had to scan the complete expression tree of held arguments passed to any function with a Hold* attribute for nested Evaluate expressions and evaluate them, and then recurse and look for Evaluate subexpressions in what it just evaluated, all while keeping the rest of the expression unevaluated, especially when this might not always be what you want to happen anyway.

Doing what you want is pretty easy using a combination of Extract and ReplacePart though:

In[51]:= expr = Hold[Plus[Plus[2, 2], 2]];

In[52]:= ReleaseHoldAt[expr_, partspec_] :=
  ReplacePart[expr, partspec -> Extract[expr, partspec]]

In[53]:= ReleaseHoldAt[expr, {1, 1}]

Out[53]= Hold[4 + 2]

This lets us illustrate another reason why it might not make sense for Evaluate to work at any level in an expression passed as an argument to a function with a Hold* attribute, considering the following expression involving i:

In[82]:= i = 1;

In[83]:= ReleaseHoldAt[Hold[i = 2; j = Plus[i, i]], {1, 2}]

Out[83]= Hold[i = 2; 2]

Note that the value of j would have been 4 if we had evaluated the first part of that expression before the Plus, but the results are different since we are only doing partial evaluation, and i=2 had not been evaluated when we evaluated the subexpression setting j. Sometimes, this may be what you want to happen, but often it is very likely not.

Keep in mind that even Evaluate in the first level can be defeated by a function that has the attribute HoldAllComplete or by using HoldComplete:

In[62]:= Hold[Evaluate[Plus[2,2]]]
Out[62]= Hold[4]

...versus:

In[63]:= HoldComplete[Evaluate[Plus[2,2]]]
Out[63]= HoldComplete[Evaluate[2+2]]

Finally, the output of Trace can be a little dense, but you can filter out what you want by using patterns or symbols of interest in the second argument:

In[88]:= Trace[Plus[Plus[Plus[1,2],3],4],Plus]
Out[88]= {{{1+2,3},3+3,6},6+4,10}

In[93]:= Trace[Plus[Subtract[Plus[1,2],4],8],_Plus]
Out[93]= {{{1+2}},-1+8}

HTH!

Michael Pilat
Thanks that helps a lot. The first function example above is simple and clever. It extracts the part of the expression you want out of an expression under the control of hold; now it is outside the range of hold so evaluates it and then places that back into the position it was taken from. I was going to try something in a longer and more complicated way.
dbjohn
+1  A: 

A technique which does not involve Extractis to wrap the parts inside Hold in inner Holds, and then release the outer Hold:

expr=Hold[(1+2)+3];
ReleaseHold@Map[Hold,expr,{2}]

Out[2]= Hold[3]+Hold[1+2]

You can play various games in this direction, but since I can't tell what it is you want to do, it is a bit hard to be specific. Something that might be useful is to define your own Hold that descents however you want it to:

SetAttributes[DescentHold,{HoldAll}]
DescentHold[a_Plus]:=ReleaseHold@Map[DescentHold,Hold[a],{2}]
DescentHold[a_]:=Hold[a]

Note that this one approaches the outer Holds once the insides are wrapped, so that for instance the flatness of Plus kicks in:

DescentHold[2*3+(4+5)]
Out[4]= Hold[4]+Hold[5]+Hold[2*3]
Janus
+2  A: 

As is so often the case when you want to do something tricky in Mathematica, pattern matching and rule replacement come to the rescue. However, in this instance, you have to do something weird, and you have to use Replace instead of ReplaceAll (the /. operator) so you can take advantage of its optional third argument to give it a level specification. Using the example you offered:

In[1]:= Replace[
         Hold[Plus[Plus[2, 2], 2]],
         expr_Plus :> With[{eval = expr}, eval /; True],
         {2}]
Out[1]= Hold[4 + 2]

The useless-looking

expr_Plus :> With[{eval = expr}, eval /; True]

rule is actually a documented way to share local variables between the test match and the body of the With structre; here you don't do anything with the local variable but force its evaluation in a roundabout way---because less roundabout ways won't work!

EDIT to add: I think you're mis-interpreting the result of Level; the two expressions at level {2} of this expression are 2 and Plus[2, 2]; you can see this by using the optional third argument to level, which does something similar to the optional third argument of Extract:

In[2]:= Level[Hold[Plus[Plus[2, 2], 2]], {2}, Hold]
Out[2]= Hold[2 + 2, 2]

With the {2} level spec, Replace will try to match-and-replace the rule against these two expressions, and it will work on the second one.

Pillsy
This might be worth its own separate question but; why does Replace work on a level specification 1 less than the level you intend? The 2nd Plus in our expression is on level 3 according to the function Level and TreeForm:`In[124]:= testExpr = Hold[Plus[Plus[2, 2], 2]]` `Out[124]= Hold[(2+2)+2]` `In[125]:= Level[testExpr, {3}, Heads -> True]` `Out[125]= {Plus,2,2}`. Yet if I use Replace for level 3 it doesn't produce the same results as level 2: `In[126]:= Replace[Hold[Plus[Plus[2, 2], 2]], expr_Plus :> With[{eval = expr}, eval /; True], {3}]` `Out[126]= Hold[(2+2)+2]`
dbjohn
@dbjohn: I hope my edit has answered the question; if not, I think this is probably worth a question of its own.
Pillsy
I can see how it can work for level 2. Shouldn't the same pattern match on level 3? Where there is a Head of Plus to match and replace or is the function Plus not "active" on this level?
dbjohn
@dbjohn: Nope. The reason that the `Plus` is popping up there on level 3 is because of the `Heads -> True` setting; you're seeing the *symbol* `Plus`, which is the head of the form `Plus[2, 2]` and is at level 3 (just like the two 2s), but `expr_Plus` will only match expressions with head `Plus`, not `Plus` itself!
Pillsy
I think I understand I probably need to see levels in terms of a tree, rather than a linear row of a full expression. The root/head/parent node (e.g. `Plus`) of the child nodes/arguments that are on level 3 is actually on level 2. So until the evaluator returns from getting the arguments on level 3 to level 2 is the expression `Plus[2,2]` formed and ready to be recognised for pattern matching. This formed expression exists on level 2 but the other child node on level 2 with a head of `Plus` on level 1 has not yet formed an expression with its head to be recognised for pattern matching. Right?
dbjohn