[Date Index]
[Thread Index]
[Author Index]
Re: Re: problem writing debugging utility function
*To*: mathgroup at smc.vnet.net
*Subject*: [mg100371] Re: [mg100308] Re: problem writing debugging utility function
*From*: Leonid Shifrin <lshifr at gmail.com>
*Date*: Tue, 2 Jun 2009 06:42:45 -0400 (EDT)
*References*: <gvq09k$mqv$1@smc.vnet.net> <200905311037.GAA17048@smc.vnet.net>
Hi,
Just to add some more to the reply of Szabolcs:
The evaluation process in Mathematica is recursive, going first "down" to
leaves, then evaluating it "up", starting from leaves and going to larger
sub-expressions, checking for the global rules to match. Unevaluated is
treated in a special way in that it suppresses the evaluation of expression
"on the way down", allowing the rules attached to the head (say <g>)
surrounding it to apply "on the way up" on the original expression. The
original expression may or may not evaluate later, depending on what <g>
does with its argument. In the following example:
In[1] =
g[x_ + y_] := x*y;
g[Unevaluated[5 + 5]]
Out[1] = 25,
the rule attached to <g> did apply since the expression 5+5 was preserved in
its unevaluated form. In this case, the original expression (Plus operator)
never evaluated, but in some other cases it may evaluate - it all depends
on <g> now. But without Unevaluated, <g> would never have a chance to see
the original expression, since by the time evaluation goes "up" to g, 5+5
would have evaluated to 10 already. This situation indeed is precisely as if
we would give <g> Hold attribute "on the way down" and remove it "on the way
up".
There are several differences between Hold and Unevaluated. Technically,
Hold is just a wrapper with a HoldAll attribute which is not particularly
different from any user-defined HoldAll wrapper, and is special just because
it is the "official" holding wrapper and there are commands such as
ReleaseHold that work with it together. Most of the functionality of Hold
can pretty much be duplicated/reproduced by any HoldAll wrapper. OTOH,
Unevaluated is one of the "magic symbols", along with Evaluate and Sequence.
It is more deeply "wired in", into the main evaluation loop algorithm, and
its functionality can not be removed, blocked or duplicated. In particular,
we may block Hold with a Block trick Block[{Hold},...], but we can not block
Unevaluated in the same way.
Another difference is that (one level of wrapping with) Unevaluated does not
by itself represent extra level of wrapping - it is automatically stripped
off when the evaluator comes to apply the rules to expression inside it. Not
so with Hold - it is a normal wrapper.
>From the viewpoint of the user, the uses of Hold and Unevaluated are also
different. Hold is often used to preserve the internal expression
unevaluated for more than one successive evaluation (or in between such
evaluations). For example, if one function (say <f>) produces the result as
an expression which wil normally evaluate but whose evaluation we want to
prevent, and another function (say <h>) needs to consume this result in this
unevaluated form, but the result is not passed directly from <f> to <h>,
then <f> may wrap the result in Hold and <h> will then have to unwrap it.
Unevaluated would not help us here since it is normally used in a single
evaluation. Here is a simple example:
In[2] =
Clear[f,h];
f[x_] := Module[{y}, y = Hold[x^2]];
h[x_Hold] := x /. Hold[a_^b_] :> {a, b}
In[3] = odd = Select[f /@ Range[10], Not[FreeQ[#, _?OddQ]] &]
Out[3] = {Hold[1^2], Hold[3^2], Hold[5^2], Hold[7^2], Hold[9^2]}
In[4] = h /@ odd
Out[4] = {{1, 2}, {3, 2}, {5, 2}, {7, 2}, {9, 2}}
What happens here is that <f> constructs some power (square) while <h>
deconstructs it to base-power pair. But <h> is not called directly on the
result of <f>, so we need to prevent expressions from evaluation in between
evaluations involving <f> and <h>. Unevaluated would not be helpful here.
Another thing worth mentioning (and more directly relevant to your example)
is the mechanics of argument - passing. Consider this function:
In[5] =
Clear[f];
f[x_] := {Hold[x],Head[x]};
It is supposed to return the held input together with its head . Let us try
In[6] = (Print["Just before calling f"]; f[#]) &[Print["*"]]
*
Just before calling f
Out[6]= {Hold[Null],Symbol}
If we attempt to force it to not evaluate the argument (Printing), in this
way:
In[7] = Function[y, (Print["Just before calling f"]; f[y]),
HoldAll][Print["*"]]
Just before calling f
*
Out[7]= {Hold[Null],Symbol}
We see that the result was indeed not evaluated before having been passed to
<f>, but it was evaluated in <f>, and the r.h.s of <f> uses an already
evaluated result. Now, it is to prevent also this evaluation of the
argument that we need Unevaluated:
In[8] =
Function[y, (Print["Just before calling f"]; f[Unevaluated[y]]),
HoldAll][Print["*"]]
Just before calling f
*
Out[8] = {Hold[Print["*"]], Symbol}
We see that the printing still takes place, and while the held part is ok,
the head part still computes the Head of the evaluated argument (that's
where the printing happens). This is because this time, Head needs
Unevaluated - we redefine <f>:
In[9] =
Clear[f];
f[x_] := {Hold[x],Head[Unevaluated[x]]};
In[10] =
Function[y, (Print["Just before calling f"]; f[Unevaluated[y]]),
HoldAll][Print["*"]]
Just before calling f
Out[10]= {Hold[Print[*]],Print}
Now we get what we want. Hopefully by this moment the pattern started to
emerge. Note that Unevaluated is unnecessary if the function itself holds
its argument, possessing one of the hold attributes:
In[11]=
ClearAll[f];
Attributes[f] = {HoldAll};
f[x_] := {Hold[x], Head[Unevaluated[x]]};
In[12] =
Function[y, (Print["Just before calling f"]; f[y]), HoldAll][Print["*"]]
Just before calling f
Out[12]= {Hold[Print[*]],Print}
It is slightly off the main line of the argument, but another common
pitfall associated with this sort of things is something like this:
In[13] = {Hold[#] &[5^2],Hold[5^2]}
Out[13] = {Hold[25],Hold[5^2]}
The #-& notation is so routinely used that one often forgets that it does
introduce an additional parameter-passing stage. This is what one should use
to avoid evaluation during that stage:
In[14] = {Function[Null, Hold[#], HoldAll][5^2], Hold[5^2]}
Out[14] = {Hold[5^2], Hold[5^2]}
Sometimes the opposite is needed however:
In[15]:=
i = 0;
Function[x, {x, x, x}, HoldAll][i++]
Out[15]= {0, 1, 2}
In[16] = i
Out[16] = 3
In[17]:=
i = 0;
Function[x, {x, x, x][i++]
Out[17]= {0, 0, 0}
In[18] = i
Out[18] = 1
In this case, chances are that the first behavior is not what was intended.
So, to summarize: Hold is mostly useful to prevent evaluation of some
results, in between some other evaluations. Unevaluated in useful to force
some function use - just once, in this particular evaluation (for its
r.h.s.) - its original (unevaluated) argument if that function does not
have a corresponding hold attribute. Hold attributes are useful for the same
thing, but 1) They are permanent, not just one-time use 2) Unevaluated
wrappers are stripped off when evaluator meets them "on the way up", so
subsequent evaluation uses already normal version of the argument - not so
with attributes 3) You may need to work with functions written by others (or
system functions), for which changing attributes even temporarily may have
undesired/unpredictable consequences - in this case Unevaluated is more
appropriate, to make up for the lacking hold attribute just once. Perhaps,
another way to put is is that attributes belong to the function, while
Unevaluated "belongs" to a particular argument (though affecting the
execution of the function on that argument as if that function had a hold
attribute).
This is probably a very long-winded way to explain these things, but I hope
it will still clarify some issues.
Best regards,
Leonid
On Sun, May 31, 2009 at 3:37 AM, dabrowsa at indiana.edu
<dabrowsa at indiana.edu>wrote:
> Thanks a lot to Leonid, Szabolcs, and Ken for their solutions. I
> liked Leonid's the best, being both much simpler and more general than
> mine.
>
> SetAttributes[ShowIt, HoldAll];
> ShowIt[code_] :=
> Module[{y},
> Print[ToString[Unevaluated[code]], " = ", y = code];
> y];
>
> My own solution was the hideous
>
> $PreRead=ReplaceAll[#,{{"dbgv","[",var_,"]"}:>{"Print","[",RowBox
> [{"\""<>var<>" = "<>"\"",",",var}],"]"}}]&;
>
> which makes dbgv work like a macro. The grotesqueness is because what
> $PreRead sees is a very raw input form, with expressions broken down
> into RowBox's. In a sense this is the most direct analog to Lisp's
> macros, but ... yechh.
>
> The Unevaluated[] function had slipped off my radar, probably because
> I never fully grokked the distinction between it and Hold[]. In fact
> I'm still a bit confused about it. At first I thought it might like
> the evaluation inhibitor ` in Lisp, but some examples disabused me of
> that.
>
> In[2]:= f@Unevaluated[5 + 6 + 7 + 8]
>
> Out[2]= f[Unevaluated[5 + 6 + 7 + 8]]
>
> In[11]:= ToString@Unevaluated[5 + 6 + 7 + 8]
>
> Out[11]= "5 + 6 + 7 + 8"
>
>
> I might have expected Out[11]= "Unevaluated[5 + 6 + 7 + 8]"
>
>
> In[13]:= sqr[x_] := x^2
>
> In[14]:= sqr@Unevaluated[5 + 6 + 7 + 8]
>
> Out[14]= 676
>
>
> I might have expected Out[14]= sqr[5 + 6 + 7 + 8] since sqr does not
> know what to do with the pattern _+_+_+_ or _+_ for that matter.
> Could anyone elucidate this for me?
>
Prev by Date:
**Re: RandomReal gets stuck**
Next by Date:
**Re: RandomReal gets stuck**
Previous by thread:
**Re: problem writing debugging utility function**
Next by thread:
**Re: problem writing debugging utility function**
| |