6

SubsetMap introduced in V12 seems like it could be useful for aggregate list/matrix operations, like the kind wanted in Elegant operations on matrix rows and columns

If you wanted to reimplement this function (both to avoid the dependence on V12 and for learning), how would you go about doing so?

My first attempt was something like:

SubsetMap[f_, expr_, pos_] := 
 ReplacePart[expr, 
  Thread[Function[{u, v}, u -> v][pos, f[expr[[pos]]]]]]

Which works for the two examples with explicit indices on the wolfram docs, but fails with the list "slice" notation (e.g. 2;;5) as well as multidimensional lists.

1110101001
  • 1,979
  • 13
  • 24

1 Answers1

6

This is a very quick-n-dirty (so expect it to break), works on the first four examples in the 12.0 documentation. I'll revisit to accommodate named range cases (All, UpTo, etc.) if/when time permits, but this should get you pointed in the right direction.

subsetmap[f_, l_, r_] := 
 ReplacePart[l, Thread[r -> f[l[[Sequence @@ #]] & /@ r]]];
subsetmap[f_, l_, r_Span] := 
 With[{t = Range @@ r}, ReplacePart[l, Thread[t -> f[l[[t]]]]]] ;

V12 examples:

SubsetMap[Reverse, {x1, x2, x3, x4, x5, x6}, {2, 4}] == 
 subsetmap[Reverse, {x1, x2, x3, x4, x5, x6}, {2, 4}]

SubsetMap[RotateLeft, {x1, x2, x3, x4, x5, x6}, {2, 4, 5}] == 
 subsetmap[RotateLeft, {x1, x2, x3, x4, x5, x6}, {2, 4, 5}]

SubsetMap[Accumulate, {x1, x2, x3, x4, x5, x6}, 2 ;; 5] == 
 subsetmap[Accumulate, {x1, x2, x3, x4, x5, x6}, 2 ;; 5]

SubsetMap[Reverse, 
  Array[Subscript[x, ##] &, {3, 3}], {{1, 1}, {2, 2}, {3, 3}}] ==
 subsetmap[Reverse, 
  Array[Subscript[x, ##] &, {3, 3}], {{1, 1}, {2, 2}, {3, 3}}]

True

True

True

True

This seems more robust, and allows spans and {All,2} flavors (named ranges). I use MapAt to tag, then process. Note that for single-dimension lists, the alternative element position format shown in the SubsetMap docs must be used, e.g., {{1},{3},{5}} for {1,3,5}. This seems to work on the examples I tried, but again, caveat lector.

subsetmap[f_, l_, r_] := Module[{h1, h2, t},
   h2 = MapAt[h1, l, r];
   t = Most /@ Position[h2, h1];
   ReplacePart[l, Thread[t -> f[l[[Sequence @@ #]] & /@ t]]]];
ciao
  • 25,774
  • 2
  • 58
  • 139
  • Very nice! There's also the degenerate case of duplicate positions for which the MapAt tries to nest h1. DeleteDuplicates on r should take care of this without affecting anything else. – 1110101001 Apr 24 '19 at 18:31