4

If I use span to assign a value to every element in a "column", it miraculously works:

arrayExample = {
   {a, b, c},
   {a, b, c},
   {a, b, c},
   {a, b, c}};
arrayExample[[;; , 3]] = d;
arrayExample
out[...]={{a, b, d}, {a, b, d}, {a, b, d}, {a, b, d}}

The third column entries all became d! Wonderful. Then I tried to do it to an indexed variable and I got a set::setps error that the indexed variable was not a variable:

indexedArrayExample[1] = {
   {a, b, c},
   {a, b, c},
   {a, b, c},
   {a, b, c}};
indexedArrayExample[1][[;; , 3]] = d;
...Set::setps: indexedArrayExample[1] in the part assignment is not a symbol.

This is not urgent because the workaround it simple. [Assign the value of the indexed variable to an unindexed one (temp=indexed...[1]), do the column assignment to temp and then assign it back to the indexed (indexed...[1]=temp). But that is not pretty.]

It seems that my question has to do with downvalues but I cannot figure how to make this work. I have gotten as far as understanding that temp has an ownvalue and that it why it can receive the assignment whereas an indexed variable has a downvalue but I do not understand how to assign "through" the indexed variable to its downvalues. The meta-question is what in the documentation would have answered my question?

Nicholas G
  • 1,981
  • 10
  • 15

2 Answers2

6

You can achieve something like this using mutation handlers (which are undocumented, so the usual warnings apply):

MakeIndexedVariable[var_] := Language`SetMutationHandler[var, indexedVariableMutator]

Attributes[indexedVariableMutator] = {HoldFirst}; indexedVariableMutator[s_[var_[[args___]], rhs_]] := Module[ {tmp = var, ret}, ret = s[tmp[[args]], rhs]; var = tmp; (* this line only works thanks to the definition below *) ret ] indexedVariableMutator[s_[var_, rhs_]] := Language`MutationFallthrough

(* register indexedArrayExample as an indexed variable *) MakeIndexedVariable[indexedArrayExample]

(* assign and modify it ) indexedArrayExample[1] = {{a, b, c}, {a, b, c}, {a, b, c}, {a, b, c}}; indexedArrayExample[1][[;; , 3]] = d ( d *)

indexedArrayExample[1] (* {{a, b, d}, {a, b, d}, {a, b, d}, {a, b, d}} *)

This still uses the trick with the temporary variable internally, but you don't have to type it every time. Essentially, we reroute any assignments to parts of the registered variable to indexedVariableMutator, which internally saves the content of the indexed variable into tmp, before doing the requested assignment. We then store the result back into the indexed variable, and return the appropriate result.

Lukas Lang
  • 33,963
  • 1
  • 51
  • 97
2

We could implement your idea (using temp variable) generally in Mathematica by manipulating built-in functions which is not a good idea.

Note that the solution is not as elaborate and safe as @LukasLang answer and is given just to show different perspectives.

Unprotect[Set];
Set /: Set[a_[b__][[c__]], d_] := (a[b] = MapAt[Function[x, Identity[d]], a[b], List[c]])
Protected[Set];

Example

b[1] = {{1, 1}, {2, 2}, {3, 3}};

b[1][[All, 2]] = 5 (Out: {{1, 5}, {2, 5}, {3, 5}} )

b[1][[;; , 1]] = 4 (Out: {{4, 5}, {4, 5}, {4, 5}} )

b[1] (Out: {{4, 5}, {4, 5}, {4, 5}} )

Ben Izd
  • 9,229
  • 1
  • 14
  • 45