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.
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!
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.
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