Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Evaluate beyond one level within Hold in Mathematica

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.

like image 544
dbjohn Avatar asked Jun 28 '10 22:06

dbjohn


2 Answers

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!

like image 145
Michael Pilat Avatar answered Oct 14 '22 10:10

Michael Pilat


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.

like image 31
Pillsy Avatar answered Oct 14 '22 11:10

Pillsy