13
$\begingroup$

Part, AppendTo, PrependTo, AddTo, etc. allow in-place modification of a list, but only Part requires that the list be referenced through a simple symbol, e.g. the following all does what you'ld expect:

foo = {1, 2, 3, 4};
AppendTo[foo, 5]

{1, 2, 3, 4, 5}

foo[[2]] = 100;
foo

{1, 100, 3, 4, 5}

But if the list reference is something more complex, then Part fails:

bar[1] = {4, 3, 2, 1};
AppendTo[bar[1], 5]

{4, 3, 2, 1, 5}

bar[1][[2]] = 100;

Set::setps: bar[1] in the part assignment is not a symbol. >>

I know there have been work-arounds posted, but does anyone know the rationale for why Part precludes this behavior?

$\endgroup$
15
$\begingroup$

The reason is that AppendTo and PrependTo actually replace the whole list with a new one, rather than only changing a single element. In that sense, they are not really "in-place". They are in-place in the sense that the result gets assigned back to the same variable.

You can see using Trace, that AppendTo and PrependTo in fact expand:

bar[1]={4,3,2,1};
Trace[AppendTo[bar[1],5]]

(* 
   {AppendTo[bar[1],5],{bar[1],{4,3,2,1}},bar[1]=Append[{4,3,2,1},5],
    {Append[{4,3,2,1},5],{4,3,2,1,5}},bar[1]={4,3,2,1,5},{4,3,2,1,5}} 
*)

and are mostly syntactic sugar on top of Set and Append / Prepend.

With Part, the situation is different, when we try to assign to parts of an expression in-place. In such a case, Part is a lower-level command (compared to most others), and only supports such operations when the expression being modified in-place is stored in a Symbol (rather than an indexed variable, as in your example, or a more general expression). This limitation must have to do with expressions being based on arrays internally, and also with efficiency (because it makes massive part assignments quite efficient). I have discussed this issue in a little more detail here.

To put it slightly differently, AppendTo and PrependTo have the same O(n) complexity as Append and Prepend, so them being in-place is a syntactic convenience, but doesn't require any new non-trivial (or lower-level) behavior from the language - and thus, in particular, can be implemented in the top-level. Part[s, pos] = newelem is an operation that we expect to have O(1) complexity, however. The top-level can only provide O(n) by doing something along the lines of bar[1] = ReplacePart[bar[1], 2 -> 100], but this is unacceptable performance-wise.

So, the language needs to do some lower-level work to make the complexity right, but then it is natural that, for a quite general language based on immutable expressions, there will be limitations like that (only Symbols allowed). If you allow other expressions (like indexed variables) to be L-values for part assignments, you'll inevitably have to allow top-level evaluation during those assignments. That, and the absence of true pass-by-reference semantics, would make both implementation much more complex, and performance in general much harder to predict. My guess is that these are some of the reasons why those limitations exist.

$\endgroup$
  • $\begingroup$ Wow! I read through the very detailed discussion you referenced, and Part makes a lot more sense now. Mma must use some sort of copy-on-modify and maybe that has something to do with the limitations on Part. Anyway, interesting to think about. $\endgroup$ – Eric Parker Jan 27 '15 at 19:50
  • $\begingroup$ @EricParker Yes, it does use copy-on-modify. $\endgroup$ – Leonid Shifrin Jan 27 '15 at 19:51
  • 1
    $\begingroup$ @EricParker In fact, here is a reference to a more detailed discussion of this. Thanks for the accept, by the way - but generally, it is a good practice to wait for some time, to encourage more answers to appear. $\endgroup$ – Leonid Shifrin Jan 27 '15 at 20:01
  • $\begingroup$ Actually it's a good practice to accept Leonid's answers immediately. That way the rest of us don't get tempted into thinking we can do better. Delusions of adequacy and all that... $\endgroup$ – Daniel Lichtblau Jan 27 '15 at 22:07
  • $\begingroup$ (I'm not really trying to discourage other responses. Often there are multiple really good answers that are complementary rather than redundant.) $\endgroup$ – Daniel Lichtblau Jan 27 '15 at 22:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.