Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Is it possible to dynamically construct arguments to With[]?

7 views
Skip to first unread message

Leo Alekseyev

unread,
Dec 17, 2009, 7:24:28 AM12/17/09
to
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 *)

Albert Retey

unread,
Dec 18, 2009, 6:22:33 AM12/18/09
to
Leo Alekseyev schrieb:

If a slightly different way to define the variable definitions is o.k.
the following would work:

Clear[aa, a];

aa = Hold[{a = 3}];

With[{vardef = Unevaluated @@ aa},
With[vardef, a*100 + a]
]

With is useful for exactly these things: insert partially evaluated
expressions into held expressions. Using With to write a With is
particulary tricky :-)


hth,

albert

dr DanW

unread,
Dec 18, 2009, 6:24:45 AM12/18/09
to
This is a good one. Often when I am working with equations I like to
keep everything symbolic until I am ready to get a numerical result,
then I substitute from a list of parameters of the form {a->2 b, b-
>3.0, "This Parameter"-> Pi, d:>6, ...}. This can get ungainly when I
have a Block[] with several expressions that I need to substitute. I
also found one case where ReplaceAll does not work, and this is
important enough to highlight:

ReplaceAll does not work with SparseArray's.

I have written the series of functions below to help me work with
ParameterList's. The function ParameterBlock[plist, expr] does what
you are asking for, and the substitution also works within
SparseArray's.

By the way, I used this package to experiment with Workbench and have
it fully implemented as a paclet, complete with Mathematica 7
compatible Doc Center files with a Tutorial and a Guide. Let me know
if you would like the complete paclet.

---- snip -----
ParameterListQ[
pl : {(Rule[(_String | _Symbol), _] |
RuleDelayed[(_String | _Symbol), _]) ...}] :=
Max[ Tally[ pl[[All, 1]] ][[All, 2]] ] == 1

ParameterListQ[___] :=
False;

ParameterList[rules___?OptionQ] :=
DeleteDuplicates[Flatten[{rules}], SameQ[First[#1], First[#2]] &]

ParameterBlock[pl_?ParameterListQ, expr_] :=
Block[ Evaluate[FilterRules[pl, _Symbol][[All, 1]]],
Replace[FilterRules[pl, _Symbol], {Rule -> Set,
RuleDelayed -> SetDelayed}, {2}, Heads -> True];
expr //. FilterRules[pl, _String]
]

ResolveParameters[rules___?OptionQ] :=
Module[ {pl},
pl = ParameterList[rules];
pl[[All, 2]] = ParameterBlock[pl, pl[[All, 1]] ];
pl
]

---- end ---

Daniel

dh

unread,
Dec 18, 2009, 6:24:56 AM12/18/09
to

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

Leonid Shifrin

unread,
Dec 19, 2009, 6:27:01 AM12/19/09
to
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 <d...@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:
>

0 new messages