Skip to main content

assignment - Why can AppendTo modify a referenced list in-place but Part cannot?



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?



Answer



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.


Comments

Popular posts from this blog

plotting - How to draw lines between specified dots on ListPlot?

I would like to create a plot where I have unconnected dots and some connected. So far, I have figured out how to draw the dots. My code is the following: ListPlot[{{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4,13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full] I have thought using ListLinePlot command, but I don't know how to specify to the command to draw only selected lines between the dots. Do have any suggestions/hints on how to do that? Thank you. Answer One possibility would be to use Epilog with Line : ListPlot[ {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {1, 4}, {2, 5}, {3, 6}, {4, 7}, {1, 7}, {2, 8}, {3, 9}, {4, 10}, {1, 10}, {2, 11}, {3, 12}, {4, 13}, {2.5, 7}}, Ticks -> {{1, 2, 3, 4}, None}, AxesStyle -> Thin, TicksStyle -> Directive[Black, Bold, 12], Mesh -> Full, Epilog -> { Line[ ...

dynamic - How can I make a clickable ArrayPlot that returns input?

I would like to create a dynamic ArrayPlot so that the rectangles, when clicked, provide the input. Can I use ArrayPlot for this? Or is there something else I should have to use? Answer ArrayPlot is much more than just a simple array like Grid : it represents a ranged 2D dataset, and its visualization can be finetuned by options like DataReversed and DataRange . These features make it quite complicated to reproduce the same layout and order with Grid . Here I offer AnnotatedArrayPlot which comes in handy when your dataset is more than just a flat 2D array. The dynamic interface allows highlighting individual cells and possibly interacting with them. AnnotatedArrayPlot works the same way as ArrayPlot and accepts the same options plus Enabled , HighlightCoordinates , HighlightStyle and HighlightElementFunction . data = {{Missing["HasSomeMoreData"], GrayLevel[ 1], {RGBColor[0, 1, 1], RGBColor[0, 0, 1], GrayLevel[1]}, RGBColor[0, 1, 0]}, {GrayLevel[0], GrayLevel...

Is there a way to do conditional matrix loop using 'continue'

I have the following: n = 3; m = 5; ww = RandomReal[{0, 0.1}, {n, n}]; uu = RandomReal[{0, 1}, {m, n}]; pp = RandomReal[{0, 1}, {n, n}]; ss = RandomInteger[{0, 5}, {m, n}]; Grid[{{"ww", "uu", "pp", "ss"}, {ww // TableForm, uu // TableForm, pp // TableForm, ss // TableForm}}, Spacings -> {5, 2}, Dividers -> All] where I would like to look at every element of matrix ss and produce a matrix tt , with zeroes at the locations in ss which have zeroes, and in all other positions do the following: tt = (-1/Subscript[ww, m]) Log[(1 - uu)/(Subscript[pp, m - 1])], where Subscript[ww, m] is the value at index of ww matrix and where Subscript[pp, m - 1] is the value at index-1 of pp matrix. So for example if the first value ever read from matrix ss happens to be 2, then value taken from matrix ww would be from the row 2, but from pp would be from row 1. Also how to tell difference between a 0 as a valid value from within the matrix elemen...