In Mathematica Cookbook recipe 2.2 I discuss a "technique" for holding arbitrary arguments. Well truth be told, this recipe is pretty lame and was not supposed to make it into the final book. This post is intended to make of for that lameness and hopefully clarify some of the bewildering multitude of ways Mathematica offers for Holding.
The Hold Family of Attributes
Mathematica's default behavior is to evaluate every expression it sees. Here is an example.
In[1]:= a=1;b=2;c=3; d= c; e:= d; f :=e;
{a,b,c, d,e, f}
Out[2]= {1,2,3,3,3,3}
Here we associate symbols a, b, c with integers 1, 2,3. We then associate d with the value of symbol c and we associate e with symbol d telling Mathematica not to evaluate d just yet by using SetDelayed (:=). We also associate f with symbol d likewise delaying evaluation. Later, when we evaluate the list containing these symbols Mathematica keeps evaluating until there is nothing left to do and we get the result as integers. By using Trace we can see the steps Mathematica goes through.
In[3]:= Trace[{a,b,c, d,e, f}]
Out[3]= {{a,1},{b,2},{c,3},{d,3},{e,d,3},{f,e,d,3},{1,2,3,3,3,3}}
Okay, simple enough. However, occasionally you want to write functions that act on expressions before they are evaluated. In fact, even if you never had a reason for doing so, Mathematica itself needs this capability. For example, Mathematica could not implement the function SetDelayed if it did not have a way of saying "don't evaluate". Rather then creating certain functions with special no-evaluating behavior, Mathematica takes a general approach via the concept of attributes. You can inspect the attributes of a symbol using the command Attributes.
In[4]:= Attributes[SetDelayed]
Out[4]= {HoldAll,Protected,SequenceHold}
Here we see SetDelayed has two attributes in the hold-family: HoldAll and SequenceHold. Let's explore these using our own symbols and SetAttributes. Here I use symbols f1, f2 and so on without associating them with values because that is not necessary to illustrate the behavior of the attributes.
In[5]:= (* Make sure these symbols have no values or attributes*)
ClearAll[f1,f2,f3,f4,f5,f6,f7] ;
In[6]:= (* Here you can see that our list of symbols evalauates as before when we wrap f1 around it*)f1[{a,b,c,d,e,f}]
Out[6]= f1[{1,2,3,3,3,3}]
In[7]:= (*Here we use the HoldAll and associate it with f2. This says that no arguement of f2 should be evaluated*)
SetAttributes[f2, HoldAll]
In[8]:= f2[{a,b,c,d,e,f}]
Out[8]= f2[{a,b,c,d,e,f}]
In[9]:= (* It does not matter how many seperate arguments are passed, they are all held*)f2[a,b,c]
Out[9]= f2[a,b,c]
Notice the difference between evaluating f1 which has no attributes and f2 which has attribute HoldAll. In essence, f2 acts the same as the built-in Mathematica command Hold.
In[10]:= Attributes[Hold]
Out[10]= {HoldAll,Protected}
Sometimes you want only the first argument to a function to be held unevaluated. For that you use attribute HoldFirst.
In[11]:= SetAttributes[f3, HoldFirst]
f3[a,b,c]
Out[12]= f3[a,2,3]
Alternatively you may want all arguments but the first to be held. Here you use HoldRest.
In[13]:= SetAttributes[f4, HoldRest]
f4[a,b,c]
Out[14]= f4[1,b,c]
So far I think the mechanics of HoldAll, HoldFirst and HoldRest should be pretty clear. Don't worry yet if you don't get why you would want to use these in your own code, I'll get to that later. Just make sure you are comfortable with the idea of using attributes to suppress Mathematica's desire to evaluate before reading on.
Okay, now I want to point out some important exceptions. Well, not really exceptions but rather clarifications. HoldAll, HoldFirst and HoldRest do not suppress every action that Mathematica takes when it sees an expression. To illustrate this, please recall that Mathematica has a long hand way of specifying a sequence of things.
In[15]:= Sequence[1,2,3]
Out[15]= Sequence[1,2,3]
A sequence is different form a list in that Mathematica will magically flatten out sequences and splice the result into any function call.
In[16]:= f1[Sequence[1,2,3]]
Out[16]= f1[1,2,3]
In[17]:= f1[Sequence[1,2,Sequence[3,4,5]],7,8,9]
Out[17]= f1[1,2,3,4,5,7,8,9]
Now, recall that f2 has attributes HoldAll. What do you think Mathematica does if we give f2 a sequence?
In[18]:= f2[Sequence[1,2,Sequence[3,4,5]],7,8,9]
Out[18]= f2[1,2,3,4,5,7,8,9]
Ah! The automatic flatting is not suppressed by HoldAll nor is it suppressed by HoldFirst or HoldRest. The flattening out of sequences is something you usually don't want to suppress. This is why it is an exception to the rule for the standard hold family of attributes. However, you may recall that SetDelayed had another hold-like attribute called SequenceHold and you can probably guess what it does!
In[19]:= SetAttributes[f5, SequenceHold]
f5[Sequence[1,2,Sequence[3,4,5]],7,8,9]
Out[20]= f5[Sequence[1,2,3,4,5],7,8,9]
Notice how the outer sequence remains. Why did the inner sequence get flattened? Simply because it is evaluated within Sequence which naturally does not have the SequenceHold attribute.
Okay, lets review. HoldAll, HoldFirst and HoldRest are attributes that suppress evaluation of specific arguments but don't suppress sequence flattening. For that you use SequenceHold. You can use one of the hold attributes together with SequenceHold to get both behaviors.
In[21]:= SetAttributes[f6, {HoldAll,SequenceHold}]
f5[Sequence[a,b,c]]
f6[Sequence[a,b,c]]
Out[22]= f5[Sequence[1,2,3]]
Out[23]= f6[Sequence[a,b,c]]
But we are not done yet! There is even a stronger form of holding that suppresses normal evaluation, sequence flattening and more! First, recall that sometimes you want to tell Mathematica that you want something to evaluate despite the presence of HoldAll and friends. To see how there can be a stronger form of holding we must first consider Evaluate.
In[24]:= f2[Evaluate[a,b,c]]
Out[24]= f2[1,2,3]
Evaluate is a way of saying to Mathematica that you know what you are doing and you want evaluation to take place despite the presence of HoldAll, etc.. This typically arises when you want to plot a function that you obtain by integration (or other function generating operations).
In[25]:= Attributes[Plot]
Out[25]= {HoldAll,Protected}
In[26]:= Plot[Evaluate[Integrate[Sin[x],x]], {x, 0, 2Pi}]
Out[26]:=
The attribute HoldAllComplete has super-powers because it can even shield evaluation by Evaluate!
In[27]:= SetAttributes[f7,HoldAllComplete]
In[28]:= f6[Evaluate[{a,b,c}]]
f7[Evaluate[{a,b,c}]]
Out[28]= f6[{1,2,3}]
Out[29]= f7[Evaluate[{a,b,c}]]
The built in command HoldComplete is the counterpart to the Hold command.
In[30]:= Attributes[Hold]
Attributes[HoldComplete]
Out[30]= {HoldAll,Protected}
Out[31]= {HoldAllComplete,Protected}
In[32]:= HoldComplete[Evaluate[{a,b,c}]]
Out[32]= HoldComplete[Evaluate[{a,b,c}]]
Functions with attribute HoldAllComplete also suppress upvalue evaluation. I am not going to discuss up upvalues here but you can refer to the Mathematica documentation or my cookbook.
HoldForm, Unevaluated, Defer oh my!
At this point you might think we have exhausted all the ways you can suppress evaluation but no. Mathematica has some more subtle ways you can keep its evaluation engine in check. Perhaps the easiest to understand is HoldForm. This command suppresses evaluation just like Hold except the command gets hidden when display in output form. The following should make the difference clear.
In[33]:= Hold[1+2]
Out[33]= Hold[1+2]
In[34]:= ReleaseHold[%]
Out[34]= 3
In[35]:= HoldForm[1+2]
Out[35]= 1+2
In[36]:= ReleaseHold[%]
Out[36]= 3
Unevaluated can be thought of as a temporary or one-shot Hold. It suppresses evaluation the first time the Mathematica evaluator sees it but not subsequent times. The following examples are a good illustration of the difference.
In[37]:= (*first I create a simple list *)
list = {1,2,3}
Out[37]= {1,2,3}
In[38]:= (*Now lets see what happens if we try to make list list self-referential*)
list[[3]] = list; list
Out[38]= {1,2,{1,2,3}}
In[39]:= (* What happens if we doo this again using Hold? *)
list = {1,2,3};
list[[3]] = Hold[list];
list
Out[41]= {1,2,Hold[list]}
In[42]:= ReleaseHold[%]
Out[42]= {1,2,{1,2,Hold[list]}}
In[43]:= ReleaseHold[%]
Out[43]= {1,2,{1,2,{1,2,Hold[list]}}}
Okay, that is somewhat interesting. Each time we invoke ReleaseHold the list expands unveiling another nested version of itself. What do you think happens if we do this experiment with Unevaluated instead?
In[44]:= list = {1, 2, 3};
list[[3]] = Unevaluated[list];
list
During evaluation of In[44]:= $RecursionLimit::reclim: Recursion depth of 256 exceeded. >>
During evaluation of In[44]:= $RecursionLimit::reclim: Recursion depth of 256 exceeded. >>
Out[46]= {1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,{1,2,Hold[{1,2,list}]}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
Oops! We created an never ending evaluation that eventually blows up. The only way for Mathematica to finally display the "result" is for it to force a Hold into the output to put the breaks on this runaway evaluation train! The reason for this behavior is that Unevaluated allowed us to suppress evaluation of list up until the point where it made the symbol list the new third element of the List associated with the symbol list but after that step, evaluation is no longer suppressed. So we have a symbol which contains a reference to itself and the evaluator can never stop until recursion limit is reached.
Defer is another function that acts like Hold but will allow evaluation each time it is presented to the front-end for evaluation via the user action of hitting Shift-Enter or selection the expression and using Evaluation In Place. We can use the self-referential example to see this. Here each step in the expansion was created by hitting Shift-Enter on the prior output.
In[47]:= list = {1, 2, 3};
list[[3]] = Defer[list];
list
Out[49]= {1,2,list}
In[50]:= {1,2,list}
Out[50]= {1,2,{1,2,list}}
In[51]:= {1,2,{1,2,list}}
Out[51]= {1,2,{1,2,{1,2,list}}}
In[52]:= {1,2,{1,2,{1,2,list}}}
Out[52]= {1,2,{1,2,{1,2,{1,2,list}}}}
So you can see Mathematica has quite a rich repertoire of functions and attributes whose sole purpose is to keep it for doing what it was designed to do - evaluate! The novice Mathematica user may be mystified by this but these features are essential to the functionality of Mathematica. In other words, the rich sets of features Mathematica provides would not be there if Mathematica did not contain mechanisms for controlling itself. Programming after all is the act where an individual exerts control over a (abstract) machine. But perhaps this explanation is a bit too metaphysical for your tastes. So lets get to some concrete examples. Consider the rich family of Plot functions which take other functions as input. Notice that these all have HoldAll as attributes. Think for a second why this is the case before reading on.
In[53]:= Attributes[{Plot, ParametricPlot, Plot3D}]
Out[53]= {{HoldAll,Protected},{HoldAll,Protected},{HoldAll,Protected}}
Functions that rely on delayed evaluation are typically those that are in essence little evaluation engines themselves. Think about plotting. It must take a function and evaluate it at various points so that it can map the values at those points to the graphics coordinates of points (ultimately pixels on the display device). These evaluations can not be interfered with by the normal evaluation process because in many cases that will change the input before the evaluator can do its thing. If this reasoning is correct than any function in Mathematica which repeatedly evaluates another function should utilize the Hold family of attributes. One of the simplest of these is Table so let's see.
In[54]:= Attributes[Table]
Out[54]= {HoldAll,Protected}
Yep. Various forms of control flow (If, Do, Switch) also must use held arguments for similar reasons. Another class of functions that hold arguments unevaluated are those that must act on symbolic values themselves. Examples are Clear and AddTo (+=). The following little program lists all such symbols in the System` context that have an attribute in the Hold family.
In[55]:= Select[Names["System`*"],Length[Intersection[Attributes[#],{HoldAll,HoldFirst,HoldRest,HoldAllComplete}]]>0&]
Out[55]= {AbortProtect,AbsoluteTiming,AddTo,And,Animate,AppendTo,Arrow3DBox,ArrowBox,Assuming,Attributes,BezierCurve3DBox,BezierCurveBox,Block,BlockRandom,BSplineCurve3DBox,BSplineCurveBox,BSplineSurface3DBox,Button,CancelButton,Catch,Check,CheckAbort,CheckAll,ChoiceButtons,CircleBox,Clear,ClearAll,ClearAttributes,Compile,CompiledFunction,CompoundExpression,Condition,ConeBox,ConsoleMessage,Context,ContinuedFractionK,ContourPlot,ContourPlot3D,Control,ControlActive,ControllerManipulate,CuboidBox,CylinderBox,Debug,DebugTag,Decrement,DefaultButton,DefaultValues,Defer,Definition,DensityPlot,Dialog,DialogInput,DialogReturn,DiskBox,DivideBy,Do,DownValues,DumpSave,Dynamic,DynamicBox,DynamicModule,DynamicModuleBox,DynamicWrapper,DynamicWrapperBox,Exists,FileName,FindArgMax,FindArgMin,FindMaximum,FindMaxValue,FindMinimum,FindMinValue,FindRoot,For,ForAll,FormatValues,FullDefinition,Function,GeometricTransformation3DBox,GeometricTransformationBox,Graphics3DBox,GraphicsBox,GraphicsComplex3DBox,GraphicsComplexBox,GraphicsGroup3DBox,GraphicsGroupBox,Hold,HoldComplete,HoldForm,HoldPattern,If,Increment,Information,InsetBox,Interpretation,InterpretationBox,Line3DBox,LineBox,LineIntegralConvolutionPlot,Literal,MakeBoxes,Manipulate,MatchLocalNameQ,MemoryConstrained,MenuItem,Message,MessageName,MessagePacket,Messages,Module,Monitor,Nand,NCache,NIntegrate,Nor,NProduct,NSum,NValues,Off,On,Or,OwnValues,ParametricPlot,ParametricPlot3D,Parenthesize,Pattern,PatternTest,Piecewise,Play,Plot,Plot3D,Point3DBox,PointBox,Polygon3DBox,PolygonBox,PreDecrement,PreemptProtect,PreIncrement,PrependTo,Product,Protect,Quiet,RasterBox,Reap,RectangleBox,Refresh,RegionPlot,RegionPlot3D,Remove,RuleCondition,RuleDelayed,SampledSoundFunction,Save,Set,SetAttributes,SetDelayed,SphereBox,Stack,StackBegin,StackComplete,StackInhibit,StreamDensityPlot,StreamPlot,SubtractFrom,SubValues,Sum,Switch,SystemException,Table,TagSet,TagSetDelayed,TagUnset,Text3DBox,TextBox,TimeConstrained,TimesBy,Timing,Trace,TraceDialog,TracePrint,TraceScan,TubeBezierCurveBox,TubeBox,TubeBSplineCurveBox,Unevaluated,Unprotect,Unset,UpSet,UpSetDelayed,UpValues,ValueQ,VectorDensityPlot,VectorPlot,VectorPlot3D,WaitUntil,Which,While,With,$ConditionHold,$Failed}
Here you can see that the ultimate form of holding, HoldAllComplete, is more rarely used and when it is it is for rather low-level functions.
In[56]:= Select[Names["System`*"],Length[Intersection[Attributes[#],{HoldAllComplete}]]>0&]
Out[56]= {DebugTag,HoldComplete,InterpretationBox,MakeBoxes,Parenthesize,PreemptProtect,SystemException,Unevaluated}
Recipe 2.2 Reconsidered
In recipe 2.2 of Mathematica Cookbook I consider if it was possible to create functions that Hold other combinations of arguments than provided by HoldAll, HoldFirst and HoldRest. The solution proposed using Hold as a pattern within the function itself. This awkward construct thus required you to use Hold when you invoked the function. To further un-motivate this I presented a rather lame example in the solution.
In[57]:= array1 = Table[0, {10}]; array2 = Table[1, {10}];
arrayAssign[Hold[a_Symbol],aIndex_,Hold[b_Symbol],bIndex_]:=
Module[{},
a[[aIndex]] = b[[bIndex]];
a[[aIndex]]]
(*Assign elements 2 through 3 in array 2 to array1 *)
arrayAssign[Hold[array1],2;;3,Hold[array2],1];
array1
Out[60]= {0,1,1,0,0,0,0,0,0,0}
There are several reasons this solution is lame. First off, the example is not at all practical. There is little reason to create a function to do this when you can do the same in a one line expression. But that could be forgiven by virtue of being a purely pedagogical example. The real flaw is that this technique requires you to use Hold at the call site which is nothing at all like the behavior of functions with attributes HoldFirst, HoldRest or HoldAll. A more sensibly way to achieve the same effect is to simply use HoldAll and force evaluation where required. So to use this somewhat useless example again...
In[61]:= array1 = Table[0, {10}]; array2 = Table[1, {10}];
SetAttributes[arrayAssign2,HoldAll];arrayAssign2[a_Symbol,aIndex_,b_Symbol,bIndex_]:=
Module[{aIndex2,bIndex2},
{aIndex2,bIndex2} = Evaluate[{aIndex,bIndex}];
a[[aIndex2]] = b[[bIndex2]];
a[[aIndex2]]]
(*Assign elements 2 through 3 in array 2 to array1 *)
arrayAssign2[array1,2;;3,array2,1];
array1
Out[64]= {0,1,1,0,0,0,0,0,0,0}
No comments:
Post a Comment