Re: Hold/Evaluate in Integrate
- To: mathgroup at yoda.physics.unc.edu
- Subject: Re: Hold/Evaluate in Integrate
- From: villegas
- Date: Sat, 11 Dec 1993 22:27:57 -0600
Daryl (dreece at cad.gatech.edu) asked: > Can anyone tell me why > > Hold[Integrate[Evaluate[x+x], x]] > > returns > > Hold[Integrate[Evaluate[x+x],x]] > > not Hold[Integrate[2x, x]]? > > I want to force evaluation of the arguments to Integrate > but not evaluate the integral. An 'Evaluate' is effectual only if it is the outer wrapper of an argument of a function. In your case, the 'Evaluate' is inside of an Integrate, so the Hold does not see it. Here is another example to illustrate when 'Evaluate' takes effect and when not: In[12]:= Hold[Evaluate[Sin[Pi]], Sin[Evaluate[Pi]] ] Out[12]= Hold[0, Sin[Evaluate[Pi]]] 'Evaluate' is a flag caught by the evaluator during the preliminaries of evaluating an expression. The standard evaluation procedure, as outlined in Appendix A.4 of the Mathematica book (p. 729), is to take an expression h[e1, e2, ..., en] and do some preliminary work on it before sending it as a whole to the evaluator. First it evaluates the head h, then the elements ei in turn. As you know, some functions such as Hold carry a hold attribute (there are three: HoldFirst, HoldRest, and HoldAll) which tells the evaluator to leave some or all of the arguments alone. If A is a function carrying the attribute HoldAll, then H[e1, e2, ..., en] will be passed on to the evaluator after evaluating only H, not the ei's. If R is a function carrying the attribute HoldRest, then R[e1, e2, ..., en] will be passed on to the evaluator after evaluating R (the head is always evaluated) and the first argument e1, but e2 through en will be left as they are. Since you sometimes want to override the hold on just one or a few of the arguments, there's a mechanism for flagging selected arguments to dictate that they should be pre-evaluated even though all the other arguments are left as they are. An argument is flagged by wrapping it in the symbol 'Evaluate'. If an argument ei doesn't have head 'Evaluate', as e2 does in H[e1, Evaluate[e2], ..., en] then ei will be left alone. The evaluator will not delve into ei to see if there are some 'Evaluate' wrappers on subexpressions inside ei. Regardless, we can make up our own flag and write a function to recognize it and deal with it when it is wrapped around deeply nested elements. Let's choose $Evaluate as our flag, since its name is similiar to the built-in flag we are used to, and it's not taken (at least not in 2.2). Our function can make use of the pattern matcher to search through an expression and locate all subexpressions that have head '$Evaluate'. For each $Evaluate subexpression, we can just extract the contents, evaluate them, and put them back where they were, but without the '$Evaluate' which was merely a flag. To replace a subexpression with something else, we use the ReplacePart function: In[14]:= ?? ReplacePart ReplacePart[expr, new, n] yields an expression in which the nth part of expr is replaced by new. ReplacePart[expr, new, {i, j, ...}] replaces the part at position {i, j, ...}. ReplacePart[expr, new, {{i1, j1, ...}, {i2, j2, ...}, ...}] replaces parts at several positions by new. Attributes[ReplacePart] = {Protected} Here's a simple application of it to replace a held expression with its evaluation: In[15]:= heldexpr = Hold[Cos[Pi/2], Sin[Pi/2]] Pi Pi Out[15]= Hold[Cos[--], Sin[--]] 2 2 In[16]:= ReplacePart[heldexpr, Sin[Pi/2], 2] Pi Out[16]= Hold[Cos[--], 1] 2 To apply this to an expression which we expect to have some $Evaluate flags on subexpressions, we first use Position to find out where the $Evaluate's are: In[17]:= Clear[a, b] In[18]:= flaggedexpr = Hold[ $Evaluate[2 - 7], {$Evaluate[a = 3], b = 7} ] Out[18]= Hold[$Evaluate[2 - 7], {$Evaluate[a = 3], b = 7}] In[19]:= evalPositions = Position[flaggedexpr, _$Evaluate] Out[19]= {{1}, {2, 1}} Once we have a list of places to evaluate and replace, we use ReplacePart to do it cumulatively: In[20]:= ReplacePart[flaggedexpr, Part[flaggedexpr, 1], {1}] Out[20]= Hold[$Evaluate[-5], {$Evaluate[a = 3], b = 7}] In[21]:= ReplacePart[%, Part[%, 2, 1], {2, 1}] Out[21]= Hold[$Evaluate[-5], {$Evaluate[3], b = 7}] Finally, we purge the $Evaluate flag from the expression: In[22]:= DeleteCases[%, $Evaluate, {0, -1}, Heads->True] Out[22]= Hold[-5, {3, b = 7}] The $Evaluate seems to have had the desired effect. The assignment 'a = 3' should have been one side effect, and 'b = 7' should not be a side effect: In[23]:= {a, b} Out[23]= {3, b} Here is a session excerpt defining EvaluateTaggedParts and the auxiliary function DestroyTag. Both functions allow you to specify a flag with the EvaluateTag option, in case you don't want to use '$Evaluate'. ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Mathematica 2.2 for NeXT Copyright 1988-93 Wolfram Research, Inc. -- NeXT graphics initialized -- In[1]:= DestroyTag[expr_] := DeleteCases[expr, $Evaluate, {0, -1}, Heads->True] In[2]:= DestroyTag[expr_, EvaluateTag -> flag_Symbol] := DeleteCases[expr, flag, {0, -1}, Heads->True] In[3]:= EvaluateTaggedParts[expr_] := DestroyTag @ Fold[ReplacePart[#1, Part[#1, Sequence @@ #2], #2]&, expr, Position[expr, _$Evaluate] ] In[4]:= EvaluateTaggedParts[expr_, EvaluateTag -> flag_Symbol] := DestroyTag[#, EvaluateTag -> flag]& @ Fold[ReplacePart[#1, Part[#1, Sequence @@ #2], #2]&, expr, Position[expr, Blank[flag]] ] In[5]:= expr = Hold[$Evaluate[2 + 18, Exp[Pi/4 I]], a = 7, $Evaluate[b = 9]] Pi I Out[5]= Hold[$Evaluate[2 + 18, Exp[----]], a = 7, $Evaluate[b = 9]] 4 In[6]:= EvaluateTaggedParts[ expr, EvaluateTag -> $Please ] Pi I Out[6]= Hold[$Evaluate[2 + 18, Exp[----]], a = 7, $Evaluate[b = 9]] 4 In[7]:= {a, b} Out[7]= {a, b} In[8]:= EvaluateTaggedParts[expr] I/4 Pi Out[8]= Hold[20, E , a = 7, 9] In[9]:= {a, b} Out[9]= {a, 9} ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ This implementation will work even if there are nested $Evaluate expressions, because of the ordering Position puts on the list it returns. If Position finds occurrences that are nested, it lists the more deeply nested first. Therefore, the cumulative replacement will work from the bottom up when there are nested chains. If it worked from the top down, then a larger expression that contained some $Evaluate's originally might get replaced with something that no longer contained $Evaluate's, or contained them at different positions. An example to show the ordering Position actually returns: In[29]:= Clear[a, b, c] In[31]:= nested = Hold[$Evaluate[2 + 3, $Evaluate[$Evaluate[a] = 7], c = 2], 6 - 3] Out[31]= Hold[$Evaluate[2 + 3, $Evaluate[$Evaluate[a] = 7], c = 2], > 6 - 3] In[32]:= Position[nested, _$Evaluate] Out[32]= {{1, 2, 1, 1}, {1, 2}, {1}} Here is a re-formulation to show what would happen if the ordering were reversed. The only change to the original function is the application of Reverse to the output of Position in the last line of the function: In[34]:= EvaluateTaggedPartsReversed[expr_] := DestroyTag @ Fold[ReplacePart[#1, Part[#1, Sequence @@ #2], #2]&, expr, Reverse @ Position[expr, _$Evaluate] ] In[35]:= EvaluateTaggedPartsReversed[ nested ] Part::partd: Part specification Hold[$Evaluate[5, $Evaluate[7], 2], 6 + <<1>>][[1,2,1,1]] is longer than depth of object. ReplacePart::part: Part {1, 2, 1, 1} of Hold[$Evaluate[5, $Evaluate[7], 2], 6 - 3] does not exist. Part::partd: Part specification Hold[5, 7, 2, 6 - 3][[1,2,1,1]] is longer than depth of object. ReplacePart::part: Part {1, 2, 1, 1} of Hold[5, 7, 2, 6 - 3] does not exist. Out[35]= ReplacePart[Hold[5, 7, 2, 6 - 3], > Hold[5, 7, 2, 6 - 3][[1,2,1,1]], {1, 2, 1, 1}] A total mess results, since the first replacement changed the expression enough that future ones didn't make sense. Robby Villegas Technical Support Wolfram Research