Re: Is it possible to dynamically construct arguments to
- To: mathgroup at smc.vnet.net
- Subject: [mg105798] Re: [mg105783] Is it possible to dynamically construct arguments to
- From: Leonid Shifrin <lshifr at gmail.com>
- Date: Sat, 19 Dec 2009 06:27:10 -0500 (EST)
- References: <hgd7ts$bn3$1@smc.vnet.net> <200912181125.GAA16684@smc.vnet.net>
Hi Leo, Somehow I missed your original post. First, let me suggest a possible refinement of Daniel's solution which will be protected against possible global values that dummy variables may have: In[1]:= a0 = 1; b0 = 2; Hold[With[{a = a0, b = b0}, a b]] /. {a0 -> 2, b0 -> 3} // ReleaseHold Out[1] = 2 Wrapping dummy variables in HoldPattern is all that is needed to make it work also in this case: In[2]:= Hold[With[{a = a0, b = b0}, a b]] /. {HoldPattern[a0] -> 2, HoldPattern[b0] -> 3} // ReleaseHold Out[2]= 6 Here are a couple of more ways, utilizing replacement rules: This will work regardless of whether or not <var> has a global value. Using a delayed rule allows us to keep the code a=3 unevaluated. In[3]:= Unevaluated[With[var, a*100]] /. HoldPattern[var] :> {a = 3} Out[3]= 300 This is the same but we can use the global rule already stored in OwnValues in this case (you must use delayed assignment though) : In[4]:= Clear[aa]; aa := {a = 3} In[5]:= Unevaluated[With[aa, a*100]] /. OwnValues[aa] Out[5]= 300 For this I may be flamed, but in this particular case it works well: In[6] = Block[{Set}, With[Evaluate[aa], a*100]] Out[6] =300; However, generally using Block trick is not a good idea because you don't fully control the evaluation stack associated with the <body> of With for generic <body> and can't be sure that Set will never be used there, and besides it is especially dangerous to Block such an important command as Set. Here is a somewhat more sophisticated approach based on a partial evaluator, which extends the above to allow you to store you initialization list also in DownValues and SubValues: ClearAll[symbolicHead]; SetAttributes[symbolicHead, HoldAll]; symbolicHead[f_Symbol[___]] := f; symbolicHead[f_[___]] := symbolicHead[f]; symbolicHead[f_] := Head[Unevaluated[f]]; ClearAll[partialEval]; SetAttributes[partialEval, HoldAll]; partialEval[a_Symbol] /; OwnValues[a] =!= {} := Unevaluated[partialEval[a]] /. OwnValues[a]; partialEval[a : f_Symbol[___]] /; DownValues[f] =!= {} := Unevaluated[partialEval[a]] /. DownValues[f]; partialEval[a_] := With[{sub = SubValues[ Evaluate[symbolicHead[a]]]}, (Unevaluated[partialEval[a]] /. sub) /; sub =!= {}]; ClearAll[myWith]; SetAttributes[myWith, HoldAll]; myWith[initvars_, body_] := With @@ Append[partialEval[initvars], Unevaluated[body]]; It allows you to store the initialization for variables separately and globally as some OwnValues, DownValues or SubValues. The usage might be as follows: In[7]:= Clear[aa,bb,cc,dd,a,b,c]; aa:={a=3}; bb[1]:={a=4}; cc[1][2]:={a=5}; dd[1][2][3]:={a=1,b=2,c=3}; In[12]:= myWith[aa,a*100] Out[12]= 300 In[13]:= myWith[bb[1],a*100] Out[13]= 400 In[14]:= myWith[cc[1][2],a*100] Out[14]= 500 In[15]:= myWith[dd[1][2][3],c*100+b*10+a] Out[15]= 321 In[16]:= {a, b, c} Out[16]= {a, b, c} It might be a good idea to add to this some warning messages when some of the variables initialized in <initvars> are not utilized in the body - without such checks breaking the scope globally is error-prone. Even with such messages breaking the scope is error-prone because you can never automatically spot it if you forgot to localize some variable and are consequently using its global value in the body of <With> (and the latter is often desirable when With is an inner construct inside Modules or other scoping constructs - then variables local to those will look global to the inner With of interest - so you can not just forbid it). So, consider all of the above suggestions as just an illustration of some additional ways to accomplish what you asked for, but not as my advice to actually start using this in place of the standard With, unless the project is small and the convenience really overweighs the risk of errors. By the way, it is a little outside of the main line of discussion, but note that With does evaluate the r.h.sides of the initialization list: In[17] = With[{a = (Print["*"]; 1)}, Hold[a]] During evaluation of In[17]:= * Out[17] = Hold[1] Usually this is what is needed. But this also means that you really *can not* directly use With to insert partially evaluated expressions into some code. The solution of Albert does indeed "blind" With to interpret the list {a=...} as initializer rather than assignment to global <a> , but it does not prevent the following: In[18]:= aa = Hold[{a = (Print["*"]; 1)}]; With[{vardef = Unevaluated @@ aa}, With[vardef, Hold[a]]] During evaluation of In[19]:= * Out[20]= Hold[1] which indicates that the r.h.s of the initializer gets evaluated also in this case. Wrapping the r.h.s in Unevaluated as in With[{a = Unevaluated[(Print["*"]; 1)]}, Hold[a]] may work in some cases though, but this must be done by hand for all initializers in the initialization list, and besides, Unevaluated wrapper will be kept inside all heads with the relevant Hold*-attributes - which may or may not be harmless). Rather, you usually use With to insert evaluated pieces into places located deep inside an otherwise unevaluated (at the stage of insertion) expression. If you want to keep your initializers' r.h.sides unevaluated but use the advantages of With over replacement rules (the capability of automatic nesting / name collision resolution with inner scoping constructs, for instance), it is also possible but less straightforward. The following wrapper can be wrapped around any With and forbid it to evaluate the expressions - r.h.sides in the initialization list: ClearAll[withNoEval]; SetAttributes[withNoEval, HoldAll]; withNoEval[With[vars_, body_]] := Module[{myHold, heldVars = Hold[vars]}, SetAttributes[myHold, HoldAll]; heldVars = heldVars /. Verbatim[Set][x_, y_] :> Set[x, myHold[y]]; With @@ Append[heldVars, Unevaluated[Unevaluated[body] /. myHold[x__] :> x]]]; For example: In[21]:= With[{a = (Print["*"]; 1), b = (Print["**"]; 2)}, Hold[a*b]] During evaluation of In[21]:= * During evaluation of In[21]:= ** Out[21]= Hold[1 x 2] In[22]:= withNoEval[ With[{a = (Print["*"]; 1), b = (Print["**"]; 2)}, Hold[a*b]]] Out[22]= Hold[(Print["*"]; 1) (Print["**"]; 2)] Here is by the way how Albert's solution can be modified to easily incorporate this functionality: In[23]:= aa = Hold[{a = (Print["*"]; 1)}]; With[{vardef = Unevaluated @@ aa}, withNoEval@With[vardef, Hold[a]]] Out[24]= Hold[Print["*"]; 1] In this form, it will generalize to several variables in the initialization list. This should be ok in most cases (not sure about heads with SequenceHold attribute possibly present in the <body> of original With) when you might need such functionality, but will cause some performance hit with respect to pure With (this solution won't work with <myWith> function above, however). Finally, if you are interested in playing with scoping (With in particular), there are a couple of threads on similar topics that I am aware of (perhaps many other threads in the past as well, of which I am not aware due to my rather young age as the group member): http://groups.google.com/group/comp.soft-sys.math.mathematica/browse_thread/thread/91ae4b08d3d27dea/ http://groups.google.com/group/comp.soft-sys.math.mathematica/browse_thread/thread/3a5ae92bda1c7511 Hope this helps. Regards, Leonid On Fri, Dec 18, 2009 at 3:25 AM, dh <dh at metrohm.com> wrote: > > > Hi Leo, > > First, Wrap the with in Hold. In the "With" write dummy values for the > > local variables. Then replace the dummys by actual values and finally > > relese the Hold: > > > > Hold[With[{a = a0, b = b0}, a b]] /. {a0 -> 2, b0 -> 3} // ReleaseHold > > > > Daniel > > > > Leo Alekseyev wrote: > > > I've been trying to construct a list of local variables for the With > > > construct dynamically, with no avail. I tried various combinations of > > > Unevaluated, Hold, and even ToBoxes/MakeExpression. I've read that > > > With[] evaluates its initialization lists in a non-standard way (cf > > > > http://library.wolfram.com/conferences/devconf99/villegas/UnevaluatedExpressions/Links/index_lnk_7.html > ) > > > -- is this the problem?.. Is it possible to achieve something like > > > the code below? > > > > > > (* this doesn't wok *) > > > Clear[aa, a]; > > > aa = List[Unevaluated[a = 3]]; > > > With[aa, a*100] // Print; (* want 300 *) > > > > >
- References:
- Re: Is it possible to dynamically construct arguments to With[]?
- From: dh <dh@metrohm.com>
- Re: Is it possible to dynamically construct arguments to With[]?