Re: Using Hold, ReleaseHold, and Unevaluated

*To*: mathgroup at smc.vnet.net*Subject*: [mg2330] Re: Using Hold, ReleaseHold, and Unevaluated*From*: wagner at goober.cs.colorado.edu (Dave Wagner)*Date*: Tue, 24 Oct 1995 02:15:32 -0400*Organization*: University of Colorado, Boulder

In article <DGvxIx.BL4 at wri.com>, Claudio Fahey <claudio at wizard.hip.berkeley.edu> wrote: > >I'm trying to write a function that constructs a pure function. I would >like to do the following (for example): > >In[1] := f = Func[{{x1,y1},{x2,y2}}, (x1-x2)^2 + (y1-y2)^2]; >In[2] := f[{{a,b}, {c,d}}] >Out[2] = (a-c)^2 + (b-d)^2 > >The function I want to write, Func, is similar to Function but it >accepts nested lists of variables as well as a flat list. > >Func[varpattern_, f_] := Module[ Evaluate[Flatten[varpattern]], > (varpattern = #; f) & ]; > >This function works fine but I would like it to have the HoldAll >attribute. Without this attribute, the following problem occurs: > >In[1] := x1 = 1.0; >In[2] := f = Func[{{x1,y1},{x2,y2}}, (x1-x2)^2 + (y1-y2)^2] >Out[2] = >Module::lvsym: > Local variable specification {2, y1, x2, y2} contains 2 > which is not a symbol or an assignment to a symbol. >Module[{2, y1, x2, y2}, > 2 2 > ({{2, y1}, {x2, y2}} = #1; (2 - x2) + (y1 - y2) ) & ] > >The x1 in the parameter list was replaced with 1.0. The HoldAll >attribute would prevent the x1 in the parameter list from being >evaluated. > >However, even with the HoldAll attribute, x1 is still being evaluated >because Evaluate is being called to flatten the nested list of >variables. I've tried various combinations of mapping Hold, >Unevaluated, and ReleaseHold but I've been unable to make Func work >properly (like Function but with nested variable lists). My first reaction was that this is a bizarre thing to want to do. I would write the example this way: f = Function[{p1,p2}, Apply[Plus, (p1-p2)^2]] but I presume you have a more complicated example for which it really does make your life easier. Anyway, this seems like something that one ought to be able to do, so I took it as a challenge. Here's my solution: In[1]:= x1 := 1. First of all, this function flattens a nested list without allowing the stuff inside of it to evaluate. It relies on the fact that Flatten takes an optional argument that tells it what head to flatten. You can simply replace all List heads with a head that holds its arguments, then Flatten w.r.t. that head. I defined my own "hold" head hh so that there'd be no confusion in case the expression being flattened contained, e.g., literal calls to Hold. In[2]:= SetAttributes[hh, HoldAll] SetAttributes[flat, HoldFirst] flat[stuff_] := Flatten[Hold[stuff] /. List->Hold, Infinity, Hold] /. Hold[x__] :> Hold[{x}] In[5]:= flat[{{x1, x2}, {{x3}, x4}}] Out[5]= Hold[{x1, x2, x3, x4}] Now we have the variable list that you want to use as the first argument to Module, with Hold wrapped around it. A general strategy for getting something like this inside of a function that holds its arguments is to Apply that function to the held expression. The Apply operation replaces the Hold head with the function that you are really interested in calling. This is complicated slightly by the fact that you want to apply a function (Module) that takes more than one argument. You do this, of course, by constructing a pure function that has just "plugs in" the argument in question. However, note that the pure function has to have a hold attribute as well; this is specified using the syntax "Function[arg, body, attribute]". The whole thing looks like this: In[6]:= SetAttributes[Func, HoldAll]; Func[varpattern_, f_] := Function[varlist, Module[ varlist, (varpattern = #; f) & ], HoldFirst ] @@ flat[varpattern] In[8]:= f = Func[{{x1,y1},{x2,y2}}, (x1-x2)^2 + (y1-y2)^2]; In[9]:= f[{{a,b},{c,d}}] Out[9]= 2 2 (a - c) + (b - d) Dave Wagner Principia Consulting (303) 786-8371 dbwagner at princon.com http://www.princon.com/princon