mathjs
mathjs copied to clipboard
splice-like function
subset is close to that, but you can't actually remove a subset from the initial matrix.
Interesting idea. Of course for an n * m matrix you can only cut out a part which either matches the width or the height of the matrix, else you can't shrink the matrix.
I need it very often, because I have buffers
buf = [ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ] ]
And I want to do a sort of destructive read. So basically popping a part of this buffer :
math.splice(buf, [[0, 1], [0, 1]], [[], []]) // [[1, 2], [5, 6]]
buf // [[3, 4], [7, 8]]
The question is ... could it be just implemented in subset ? Since it already does pretty much that. So as you said it could just support shrinking the matrix!
For the (internal) implementation I think we can use the same code for the biggest part, though for the user we should offer two separate functions subset and splice.
I really like this idea of a splice function for n-dimensional matrices, it's brilliant and fully in the spirit of JavaScript's Arrays.
This is a specific, cogent, and apparently worthwhile proposal (judging by the number of times related/variant issues have come up). To make it quite precise, if X is an Index object that has the full extent of a collection (Matrix/Array) in all but one dimension (we will use the example that it consists of p m-by-n "planes" of an l-by-m-by-n collection where p<l, although it could be a different number of dimensions or a different dimension that is not full, then the replacement R in a subset expression
M.subset(X,R)
[a.k.a M[X] = R in the internal parsed mathjs language] ought to be able to consist of q m-by-n planes where q does not necessarily equal p. In particular q = 0 (presumably represented by R just being the empty collection) should be allowed in all such indexing and when the p planes in X are contiguous, then any q should be allowed, in which case the result should be the planes of M preceding X concatenated with R concatenated with the planes of M following the extent of X.
As Jos has mentioned, this should not be difficult to implement; and I don't particularly see why it warrants a different operator name, as subset already allows replacement and this is a natural and compatible extension of that replacement semantics.
Thanks for your inputs Glen. I'm not sure I understand your proposal here. Right now, subset can be used to copy a part of a Matrix into a new Matrix, or to replace a part of a Matrix with new contents. The JavaScript splice function is to delete and/or insert a part of a matrix. I guess we could extend the existing subset for that, with a flag or so. But it feels to me like that it would be helpful to create a separate method named splice for it.
The point of the original proposal seems to be, and this is what I tried to capture in my more precise version, is that replacing part of a Matrix with nothing deletes that part of the matrix, and replacing part of a Matrix with differently- (but compatibly-) sized contents in effect inserts part of a matrix. So without change to the "left-hand-side" of a subset operation, we get these abilities by being more permissive on the "right-hand-side". And my latest post was to try to pin down exactly when such a non-identical-shape subset "assignment" is legal.
The one case of splice specifically that does not seem to be covered by this proposal is insertion of new entries in the middle of a matrix, because there isn't any way to specify an empty subset of a matrix at a certain position. I.e., by this proposal you could change M = [11,12,13,14,15] into [11,12,7,8,9,14,15] with M[3] = [7,8,9] but what would you put on the left-hand side to transform it to [11,12,13,7,8,9,14,15], i.e. not removing anything, only inserting? This case isn't covered because of the (perfectly reasonable from a "comfortable interface" point of view) decision for mathjs ranges to be inclusive at both ends. (With "right-exclusive" ranges like in Python you could presumably write M[3:3] = [7,8,9] to do a pure insertion at the third position, since the range 3:3 gives you a location but doesn't actually include any indices.) So to cover all the bases, one could imagine some possibilities (just brainstorming here):
-
Adopting this proposal for the RHS of a subset assignment not to have to perfectly match the size of the LHS, just be conformant in the way described above, and add an
insertmethod for "pure" insertion, or -
Adopting this proposal and designating a "special" range denoting "no entries, but at a particular position", such as
M[3:2] = [7,8,9]for the desired pure insertion, or -
Leave subset just as it is now, and add a new method
splicewhich takes a dimension (defaulting to the first, i.e. index in a 1-d array, the rows dimension in 2-d, the "planes" in 3-d, etc), a start position in that dimension, a count of (consecutive) positions in that dimension to remove (defaulting to 0, for pure insertion), and new contents to insert into the place indicated by the start position (defaulting to nothing for pure deletion).
I had shied away from something like (3) when commenting yesterday because the formulations I was thinking of seemed to overlap way too much with the existing functionality of "subset", but now the specific API in this version of (3) actually doesn't seem to overlap too much and seems pretty clean. And it has the advantage that assignment-like statements in the parser can't change the size of the matrix; any size-changing operations would be flagged by a long-form function call, in the parser M.splice(3,0,[7,8,9]) to alter M in place or splice(M, 3, 0, [7,8,9]) to return the result of the splice without changing M, as opposed to M[2:4] = [7,8,9] to change the middle three entries in place (resulting in [11,7,8,9,15]) or subset(M, index(2:4), [7,8,9]) to return the result of that change without actually changing M itself.
So with that reflection, my vote is now for option (3) which I think is very much in the spirit of the original poster's request without being too duplicative of subset (as the OP also worried).
The point of the original proposal seems to be, and this is what I tried to capture in my more precise version, is that replacing part of a Matrix with nothing deletes that part of the matrix, and replacing part of a Matrix with differently- (but compatibly-) sized contents in effect inserts part of a matrix. So without change to the "left-hand-side" of a subset operation, we get these abilities by being more permissive on the "right-hand-side". And my latest post was to try to pin down exactly when such a non-identical-shape subset "assignment" is legal.
Yes, that is true. Well explained. So it boils down to whether we want to extend subset to not only allow a replacement of the same size, but also allow the replacement to be smaller or larger in size. Indeed, having a clear syntax is then the challenge.
I have the feeling that allowing a special notation like M[3:2] = [7,8,9] will be confusing to end users. It makes sense to me to create an explicit function for it, like splice, and/or possibly some simpler functions like insert and replace, remove.