MathGroup Archive 2011

[Date Index] [Thread Index] [Author Index]

Search the Archive

Re: Question on Unevaluated

  • To: mathgroup at smc.vnet.net
  • Subject: [mg117396] Re: Question on Unevaluated
  • From: Leonid Shifrin <lshifr at gmail.com>
  • Date: Thu, 17 Mar 2011 06:31:56 -0500 (EST)

Alexey,


> It seems that DownValues can help to ensure that new definition will be on
> top:
>
> DownValues[f] = Prepend[DownValues[f], < new pattern >];
>

Yes, sure, direct manipulations with the DownValues are always an option. I
just prefer to not do this
when it is not necessary, and in our context I believe it is not.


>
> Very interesting! I do not understand why this happens at all. How does it
> work?
>

This must have been a design decision. This also has to do with the
semantics of both Block
(dynamic scoping) and local variables shared between the body and the
condition. My feeling is that
this is a unique language feature, in the sense that it can not be easily
reproduced or imitated
by other means.


> Is there a way to generalize this for preventing infinite recursion in
> arbitrary case, say classical
> x=x+1
> ?
>

Well, this particular one  is easy, and you know the trick already:

In[19]:= Clear[x];
Module[{tried}, x := Block[{tried = True}, x + 1] /; ! TrueQ[tried]]

In[21]:= x

Out[21]= 1 + x

Cheers,
Leonid



>
> Alexey
>
>
>  ----- Original Message -----
>  From: Leonid Shifrin
>   To: Alexey Popkov ; mathgroup at smc.vnet.net
>   Sent: Wednesday, March 16, 2011 2:49 AM
>  Subject: Re: [mg117264] Question on Unevaluated
>
>
>  Alexey,
>
>
>       I have two quesions regarding your function makeHoldN:
>
>    1) It works well in simplest cases even if makeHoldN is called before
> defining a function:
>
>    In[13]:= ClearAll[ff];
>    makeHoldN[ff,3];
>    ff[args___]:=Hold[args];
>
>    ff[1^2,2^2,3^2]
>
>    Out[16]= Hold[1,4,3^2]
>
>    Probably conflicts are only possible when the target function has
> definitions for which Mathematica's rule ordering
>    system cannot make conclusions about their generality?
>
>  Yes. Those may end up sitting on top of that added by makeHoldN. The
> problem for them will be not only that
>  calls matching their patterns will not be intercepted, but also that they
> will still be evaluated with HoldAll
>  attribute, which may not be desirable/intended (makeHoldN effectively
> releases all arguments except n-th).
>
>    Does calling makeHoldN after making all definitions always guarantee
> that the new definition will be on top?
>
>
>  Actually, I made a mistake in that statement - the call must be the first
> one, not the last one - sorry about that.
>  In fact, this is good, since it is always better to set Attributes before
> we make definitions, especially for Hold-
>   attributes. Otherwise, quite often some definitions partially evaluate
> during the time they are entered (because Set and SetDelayed partially
> evaluate the l.h.s., I think contrary to some statements in the docs), which
> may result in very subtle bugs. Regarding the guarantees - I can not give
> them, but as far as I know, yes, the new definition will be on top, except
> possibly for definitions  containing  neither patterns nor conditions, such
> as memoized definitions like HoldPattern[f[2]]:>4  - they will be higher in
> the list. There is a way to force these to also go below the one added by
> make HoldN - for example, when you create them, add a dummy condition like
> HoldPattern[f[2]]:>4 /;(someSymbol;True). This will however negatively
> impact the  performance - without conditions such no-pattern definitions are
> stored in a hash table and are (much) more efficient. But, anyway, this is a
> rather special case.
>
>    How it could be checked?
>
>  Well, you can always query the function information with ? and see the
> order of the definitions. If your question
>  is how to ensure the generality of this behavior - I don't know, but I use
> this kind of tricks routinely and had no
>  problems with this. An informal proof of this behavior can go along these
> lines:
>
>  1. It is necessarily impossible for the Mathematica's rule-ordering system
> to determine relative generality of 2 rules where at least one contains a
> condition attached, with a user-defined symbol. The reason is that the fact
> of the match of such a rule depends on the global state and can only be
> determined at run-time.
>
>  2. When Mathematica can not determine the relative generality of some
> rules, it keeps them in the order they were entered - I think this statement
> can be found in the documentation somewhere.
>
>  I do not exclude the possibility for some exceptions, but generally this
> should work and my experience confirms that.
>
>
>    2) I do not well understand for what
>
>    /; Hold[result] =!= Hold[f[args]]]]) /;
>       FreeQ[DownValues[f], keepOnTop]]
>
>    is added. In simple cases makeHoldN works well wihout it.
>
>  Indeed, the line Hold[result] =!= Hold[f[args]] can be left out - this is
> a remnant of my intermediate implementation.
>  But the reason that we can do this appears quite subtle. Once the function
> (say f[1^2,2^2,3^2])  computes into
>  f[1,2^2,9], a new attempt to compute f is of course made. But now, it is
> made from within the Block scope, and
>  the definition does not match since keepOnTop is now False. The
> non-trivial part here is that the new evaluation
>   is not attempted after we leave the Block scope - this is why there is
> noinfinite recursion. I think that this, more
>   subtle part of the trick, I did not realize before (this looks different
> from the version of the trick with overloading
>  the built-ins, where the system definitions *are* used even when the
> function ends up being returned "unevaluated"  - say due to unknown number
> and/or types of arguments).
>
>  The line FreeQ[DownValues[f], keepOnTop]] is needed to not add a new
> definition more than once.
>
>  Regards,
>  Leonid
>
>
>
>    Alexey
>      ----- Original Message -----
>      From: Leonid Shifrin
>       To: Alexey Popkov ; mathgroup at smc.vnet.net
>      Sent: Monday, March 14, 2011 2:40 PM
>      Subject: Re: [mg117264] Question on Unevaluated
>
>
>      Alexey,
>
>      Doing what you request is generally not possible or at least extremely
> hard (emulating exact behavior of Unevaluated in all cases), since
> Unevaluated is one of the "magic symbols" (together with Evaluate and
> Sequence), wired deep into the system. I discuss this a bit more here:
>
>
> http://stackoverflow.com/questions/4856177/preventing-evaluation-of-mathematica-expressions/4856721#4856721
>
>      Regarding your particular request: it is an interesting exercise in
> working with held expressions. Assuming
>      that we can only use Hold attributes, but neither Unevaluated nor
> Evaluate, the following function will
>      (hopefully) work as if you had an attribute HoldN (that is, n-th
> argument held, others not):
>
>      joinHeld[a__Hold] :=
>        Hold @@ Replace[Hold[a], Hold[x___] :> Sequence[x], {1}];
>
>      splitHeldSequence[Hold[seq___], f_: Hold] := List @@ Map[f,
> Hold[seq]];
>
>      Clear[makeHoldN];
>      Module[{keepOnTop},
>       makeHoldN[f_, n_Integer] :=
>        (SetAttributes[f, HoldAll];
>          f[args___] /; (! TrueQ[! keepOnTop]) :=
>           Block[{keepOnTop = False},
>            With[{result =
>               If[n > Length[Hold[args]],
>                f @@ {args},
>                Apply[f,
>                 joinHeld @@
>                    Flatten[{Hold[##] & @@@ Take[#, n - 1], #[[n]],
>                      Hold[##] & @@@ Drop[#, n]}] &[
>                  splitHeldSequence@Hold[args]]]]},
>             result /; Hold[result] =!= Hold[f[args]]]]) /;
>         FreeQ[DownValues[f], keepOnTop]]
>
>      Here are some examples of use:
>
>      ClearAll[f]
>      makeHoldN[f, 2];
>      f[1^2, 2^2, 3^2]
>
>      f[1, 2^2, 9]
>
>
>      In[22]:= ClearAll[ff];
>      ff[args___] := Hold[args];
>      makeHoldN[ff, 3];
>      ff[1^2, 2^2, 3^2]
>
>      Out[25]= Hold[1, 4, 3^2]
>
>      One can rather easily generalize this to hold an arbitrary subsequence
> of arguments (specified by
>      a list of their indices) while evaluating the rest. The implementation
> employs a number of tricks.
>      One that needs a bit of clarification is the f[args___] /; (!
> TrueQ[!keepOnTop]) line, since it serves
>      2 purposes. The one related to Block trick is well-known to you. The
> other is that the presence of
>      condition involving a user-defined symbol makes it impossible for
> Mathematica rule ordering
>      system to make conclusions about the generality of the rule, and
> therefore the rule does not go
>      to the bottom of the rule list. This is needed because we want this
> rule to stay at the top, to intercept
>      all calls to the function. For the same reason, makeHoldN should be
> called already after all the
>      definitions have been given to the function, or the function will not
> work properly.
>
>      As you see, this is sort of possible, but complex and error-prone.  In
> practice, it is best to avoid
>      this sort of trickery, by changing the design of your functions. In my
> experience, having HoldFirst,
>      HoldRest and HoldAll is quite enough. To evaluate any held argument,
> you can also wrap it in
>      Evaluate. So, you particular question can be answered quite easily
> also as
>
>      SetAttributes[f,HoldAll];
>      f[Evaluate[Print[1]], Print[2], Evaluate[Print[3]]]
>
>      Note also, that I don't claim to reproduce exactly the behavior of
> Unevaluated with my function above.
>      It is just an illustration of a possible poor man's device to
> accomplish the specific goal of holding
>      n-th argument without the help of Evaluate and Unevaluated.
>
>      HTH
>
>      Regards,
>      Leonid
>
>
>
>      On Mon, Mar 14, 2011 at 1:06 AM, Alexey Popkov <lehin.p at gmail.com>
> wrote:
>
>        Leonid,
>
>         Is it possible to imitate the behavior of Unevaluated by setting At
> tributes in this case:
>
>        f[Print[1], Unevaluated[Print[2]], Print[3]]
>
>        ?
>
>        I am wondering, what attributes are temporarily set when we use
> Unevaluated and how could I imitate this?
>
>        Alexey
>
>
>          ----- Original Message -----
>          From: Leonid Shifrin
>           To: Alexey ; mathgroup at smc.vnet.net
>          Sent: Monday, March 14, 2011 1:39 AM
>          Subject: Re: [mg117264] Question on Unevaluated
>
>
>          Alexey,
>
>          You forgot about the CompoundExpression (;). You only attempted to
> prevent the evaluation of 1+1 inside
>          (1+1;3), but not the total result for CompoundExpression, which is
> the value of the last statement
>          (2+1 in this case). This is what you probably had in mind:
>
>          In[9]:= f[Unevaluated[(1 + 1; 2 + 1)]]
>
>          Out[9]= f[Unevaluated[1 + 1; 2 + 1]]
>
>          What is perhaps less obvious is that you did not prevent the
> evaluation of 1+1 either. Here is
>          a simple way to check it:
>
>          In[14]:= f[Unevaluated[Print["*"]]; 2 + 1]
>
>          During evaluation of In[14]:= *
>
>          Out[14]= f[3]
>
>          The problem is that Unevaluated is only effective once. To totally
> prevent something from evaluation,
>          you have to know the exact number of sub-evaluations (which is
> generally impossible to know since
>          it can be data-dependent), and wrap in as many levels of
> Unevaluated. In this case,  the following will do:
>
>          In[13]:= f[Unevaluated[Unevaluated[Print["*"]]]; 2 + 1]
>
>          Out[13]= f[3]
>
>          But as I said, this is not a robust approach, and in such cases
> you will be better to use Hold or similar for
>          a persistent holding wrapper, stripping it off  later when needed.
> You may want to check out e.g. this thread
>
>
> http://groups.google.com/group/comp.soft-sys.math.mathematica/browse_thread/thread/bfd67e9122b1fdec
>
>          (my second post there), where I elaborate on these issues.
>
>          HTH.
>
>          Regards,
>          Leonid
>
>
>
>
>          On Sun, Mar 13, 2011 at 1:26 PM, Alexey <lehin.p at gmail.com>
> wrote:
>
>            Hello,
>
>            I am puzzled a bit by the Documentation for Unevaluated. Under
> "More
>            information" field we read:
>
>            "f[Unevaluated[expr]] effectively works by temporarily setting
>            attributes so that f holds its argument unevaluated, then
> evaluating
>            f[expr].".
>
>            After reading this I expect that
>
>            f[Unevaluated[1 + 1]; 2 + 1]
>
>            will be returned completely unevaluated as it is when I set
> HoldFirst
>            attribute to f:
>
>            In[2]:= SetAttributes[f, HoldFirst]
>            f[Unevaluated[1 + 1]; 2 + 1]
>
>            Out[3]= f[Unevaluated[1 + 1]; 2 + 1]
>
>            But in really we get
>
>            In[1]:= f[Unevaluated[1 + 1]; 2 + 1]
>
>            Out[1]= f[3]
>
>            This leads me to a question: what is implied in documentation?
> Which
>            attributes are temporarily set and to which function?
>
>


  • Prev by Date: Re: Wolfram, meet Stefan and Boltzmann
  • Next by Date: Re: Question on Unevaluated
  • Previous by thread: Re: Question on Unevaluated
  • Next by thread: Re: Question on Unevaluated