MathGroup Archive 2011

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

Search the Archive

Re: Assigning part of indexed object

  • To: mathgroup at smc.vnet.net
  • Subject: [mg119950] Re: Assigning part of indexed object
  • From: "Oleksandr Rasputinov" <oleksandr_rasputinov at hmamail.com>
  • Date: Thu, 30 Jun 2011 20:42:01 -0400 (EDT)
  • References: <itsjn7$8u7$1@smc.vnet.net> <iug5k3$9fi$1@smc.vnet.net>

Leonid,

On Wed, 29 Jun 2011 22:31:47 +0100, Leonid Shifrin <lshifr at gmail.com>
wrote:

> Oleksandr,
>
> While your latest fix seems to work (except that it does not solve the  
> O(n) problem
> for part assignments of  arrays stored in indexed variables, but that at
> least is not possible
> to do without this code anyway, so no existing code can be broken. You  
> also could have included
> the multi-index parts), I would not use it anyway.
>
> In Mathematica, Hold-attributes give one the ability to make custom  
> scoping constructs and
> assignment operators, a luxury that few languages offer. Overloading Set
> (particularly by adding
> DownValues to Set) therefore is, in the vast majority of cases, not a
> necessity but just a
> notational convenience, which one should weight against completely
> unpredictable consequences of
> redefing one of the most fundamental operations. And it does not matter  
> how carefully you use it, since there is a lot of top-level code in  
> Mathematica itself, and you will
> never be able to guarantee that that code won't break. So the conclusion
> should be
> clear - create your own data type and assignment operators if you need.

Just to mention one other possibility, whereby overloading Set using
DownValues can be made safe by having the definitions apply only to
certain symbols using ideas from object-oriented programming. This
approach might even provide greater safety than the method you outline
below because it is symbol- (rather than scope-)specific. Of course
this is not to detract from your suggestions, which constitute an
excellent example of good practice in Mathematica.

Using indexedQ from before (which certainly should also be
SequenceHold, although HoldAllComplete seems perhaps excessive):

indexedSymbol /: new[indexedSymbol] :=
   Module[{indexedSymbol},

    (* Initialization *)
    Unprotect[Set];
    Set[sym_indexedSymbol?indexedQ[[part_]], val_] := (
      sym = ReplacePart[sym, part -> val];
      sym[[part]]
     );
    Protect[Set];

    (* Destructor *)
    indexedSymbol /: Clear[pre___, indexedSymbol, seq___] := (
      Unprotect[Set];
      Set[sym_indexedSymbol?indexedQ[[part_]], val_] =.;
      Protect[Set];

      indexedSymbol /: Clear[$pre___, indexedSymbol, $seq___] =.;
      Clear[pre, indexedSymbol, seq]
     );

    indexedSymbol
   ];

Usage is then:

In[3] :=
f = new[indexedSymbol];
With[{f = f},
   f[1] = Range[5];
   f[1][[1]] = 10;
   f[1]
  ]

Out[3] =
{10, 2, 3, 4, 5}

while behaviour for all other symbols remains unchanged. The
indexedSymbol object(s), when no longer required, are deleted using:

Clear[Evaluate[f], f];

At this point however we have a construct that is a better programming
exercise than it is a useful solution, given that it is more
complicated than using ReplacePart directly.

Best,

O. R.


> Too bad that
> Mathematica's grammar is fixed and currently no tools like preprocessor  
> or programmable
> reader exist for it to make the code look prettier, but I think one can  
> live with that.
>
> I must admit that I am myself guilty, having overloaded Set and  
> SetDelayed a few times, like e.g. here:
>
> http://groups.google.com/group/comp.soft-sys.math.mathematica/browse_thread/thread/c000439b48751078
>
> but I did it for very special purposes when I needed to "spy" on  
> defintions given when loading
> packages, or modify them on the fly, and these were mostly for  
> functions, so more like a one-time
> affair. And I am not fond of those hacks either.
>
> Besides, Part assignment is anyway special and already overloaded in
> Mathematica,
> since for a generic head h which does not have Hold* Attributes, in an
> assignment like
>
> h[x] = value
>
> both the head h, and value x, *are*evaluated, while rules for h[ _ ] are  
> not applied
> (the fact that is not fully reflected in the documentation, which states
> this for <x> but not for <h>).
> But clearly, this is not what is happening in the case of  Part, since  
> Part is not HoldFirst and
> therefore in expr[[part]] = value, expr should normally have been
> evaluated (and then would of
> course no longer represent the L - value). So, by overloading Part
> assignment, you not only
> overload Set, but overload the already overloaded and not completely
> specified part of Set definitions.
>
> As an alternative to a custom part assignment operator, one can use a  
> custom wrapper for a part
> assignment, at the expense of a slight notational inconvenience. For
> example, like this (using your
> indexedQ - which, to be pedantic, better be HoldAllComplete rather than
> HoldFirst):
>
> ClearAll[part];
> part /: Set[part[sym_?indexedQ, part__], value_] :=
>   (
>    sym = ReplacePart[sym, part -> value];
>    sym[[part]]
>    );
> part /: Set[part[sym_, part_], value_] := sym[[part]] = value;
> part[expr_, part__] := Part[expr, part];
>
> Since Set does not apply the rules for the head of its l.h.s.,  the
> DownValues for
> <part> won't fire before UpValues when we use <part> inside Set. Example  
> of
> use:
>
>
> In[111]:= Clear[f];
> f[1] = Range[10];
> part[f[1], 3]
>
> Out[113]= 3
>
> In[116]:= part[f[1], 3] = 10;
> part[f[1], 3]
>
> Out[117]= 10
>
> This immediately removes the main problem, since Set is now "softly"
> overloaded via UpValues.
> One can get fancier and then make a custom "scoping construct", which  
> would lexically or dynamically
> change Part to part in a piece of code. Inside it, one can then use the
> exact same syntax as before,
> without any changes. Here is the lexical "scoping construct", for  
> example:
>
> ClearAll[withCustomPartLex];
> SetAttributes[withCustomPartLex, HoldAll];
> withCustomPartLex[code_] := With[{Part = part}, code];
>
> Example of use:
>
> withCustomPartLex[
>  Clear[f];
>  f[1] = Range[10];
>  f[1][[3]]  = 10;
>  f[1]]
>
> {1, 2, 10, 4, 5, 6, 7, 8, 9, 10}
>
> And all is safe. The O(n) problem for assignments to parts of indexed
> variables still not solved, since the same ReplacePart-based  
> implementation
> is used, but this is a separate topic. One way to solve it is to use the
> temporary symbols generated during the first assignment to an indexed
> variable, to store the data. One will have to deallocate those however.
>
> Anyways, my main message is that even within what is available at the
> top-level and already parsed Mathematica code, Mathematica  
> metaprogramming
> facilities are rich enough that one may, in most cases (including this  
> one
> IMO), satisfy one's taste in terms of both syntax and semantics, without
> playing games with crucial and heavily used system functions.
>
> Regards,
> Leonid


  • Prev by Date: Re: Is the iterated continued fraction from convergents for Pi/2 equal to 3/2?
  • Previous by thread: Re: Assigning part of indexed object
  • Next by thread: Export a Grid[] object without loosing it's format