Dear all, In swift (just as in many other languages) I have been terribly missing the operators like .* ./ .^ as I know them from MATLAB/Scilab. These operators are very handy and do element-wise operations on vectors or matrices of the same size.
So for instance A*B is a matrix multiplication (and the number of columns for A must correspond to the number of rows in B), whereas A*B (with A and B of same size) returns the matrix of that size whose elements are obtained by making the product of each pair of elements at the same location in A and B. So just a small example: [1.0 , 2.5 , 3.0] .* [2.0 , 5.0 , -1.0] -> [2.0 , 12.5 , -3.0] The same exists for the division (./) or for instance for the power function (.^). Here another example with *, .* , ^ , and .^ to show the difference in behaviour in MATLAB/Scilab >> A = [1 2 3 ; 4 5 6 ; 7 8 9]; >> A*A ans = 30 36 42 66 81 96 102 126 150 >> A.*A ans = 1 4 9 16 25 36 49 64 81 >> A^2 ans = 30 36 42 66 81 96 102 126 150 >> A.^3 ans = 1 8 27 64 125 216 343 512 729 For addition and subtraction the regular operator (+ and -) and their counterparts (.+ and .-) are actually doing the same. However note that since the + operator on arrays is defined differently (it does an append operation), there is a clear use for a .+ operation in swift. Version 1: In principle, we can define it recursively, for instance ...+ would be the element-wise application of the ..+ operator, which is itself the element-wise application of the .+ operator, which is also the element-wise application of the + operator. Version 2: Alternatively we could have a concept where .+ is the element-wise application of the .+ operator and finally when reaching the basic type (e.g. Double when starting from [[[[Double]]]]) the .+ operator needs to be defined as identical to the + operator. I do prefer this version since it does not need to define various operators depending on the "level" (i.e. Double -> level 0, [Double] -> level 1, [[Double]] -> level 2, etc.). I could make this option work without generics, but as I tried it with generics it generated a runtime error as the call stack grew indefinitely (which does not seem as something that should actually happen since at each call the level gets lower and when reaching 0 it all solvable). Anyway, I would like to discuss first the basic idea of defining these element-wise operators for Arrays, before seeing how far it would be interesting to go on this and how the implementation should exactly look like. As a support for the discussion, you will find hereunder a first shot for a generics-based solution for the aforementioned Version 1 and going up to level 3 and for the 4 basic operators + - * / (BTW you can see that I have twice the same code, once with the protocol conformance to my own protocols and once for FloatingPoint: is there a way to specific the protocol conformance to protocol A or to protocol B at once?) I personally think that these operators are very practical and helping programmers to directly "vectorize" the way they write their operations. Often these element-wise operations replace loops and I think that the required syntax analysis (on compiler's side) to vectorize the code is much simpler then. In swift, I have been using map, flatMap, zip + map with a closure to make these type of operations, but I think that the proposed operators would be a much clearer and expressive way of coding this for most basic operations. Note that I mention and consider only Arrays here, but the idea might be extended to other collections/containers. I am very curious to see the feedback of the community on this! Nicolas infix operator .+ infix operator ..+ infix operator ...+ infix operator .- infix operator ..- infix operator ...- infix operator .* infix operator ..* infix operator ...* infix operator ./ infix operator ../ infix operator .../ protocol ImplementsInnerAddition { static func + (_: Self,_: Self)->Self } protocol ImplementsInnerSubtraction { static func - (_: Self,_: Self)->Self } protocol ImplementsInnerMultiplication { static func * (_: Self,_: Self)->Self } protocol ImplementsInnerDivision { static func / (_: Self,_: Self)->Self } func .+<T> (lhs: [T], rhs: [T]) -> [T] where T:ImplementsInnerAddition { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: T,b: T)->T in return a + b }) } guard (lhs.count != 1) else { return rhs.map({(b: T)->T in return lhs[0] + b }) } guard (rhs.count != 1) else { return lhs.map({(a: T)->T in return a + rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func .+<T> (lhs: [T], rhs: T ) -> [T] where T:ImplementsInnerAddition { return lhs.map({(a: T)->T in return a + rhs }) } func .+<T> (lhs: T , rhs: [T]) -> [T] where T:ImplementsInnerAddition { return rhs.map({(b: T)->T in return lhs + b }) } func ..+<T> (lhs: [[T]], rhs: [[T]]) -> [[T]] where T:ImplementsInnerAddition { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: [T],b: [T])->[T] in return a .+ b }) } guard (lhs.count != 1) else { return rhs.map({(b: [T])->[T] in return lhs[0] .+ b }) } guard (rhs.count != 1) else { return lhs.map({(a: [T])->[T] in return a .+ rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func ..+<T> (lhs: [[T]], rhs: [T] ) -> [[T]] where T:ImplementsInnerAddition { return lhs.map({(a: [T])->[T] in return a .+ rhs }) } func ..+<T> (lhs: [T] , rhs: [[T]]) -> [[T]] where T:ImplementsInnerAddition { return rhs.map({(b: [T])->[T] in return lhs .+ b }) } func ...+<T> (lhs: [[[T]]], rhs: [[[T]]]) -> [[[T]]] where T:ImplementsInnerAddition { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: [[T]],b: [[T]])->[[T]] in return a ..+ b }) } guard (lhs.count != 1) else { return rhs.map({(b: [[T]])->[[T]] in return lhs[0] ..+ b }) } guard (rhs.count != 1) else { return lhs.map({(a: [[T]])->[[T]] in return a ..+ rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func ...+<T> (lhs: [[[T]]], rhs: [[T]] ) -> [[[T]]] where T:ImplementsInnerAddition { return lhs.map({(a: [[T]])->[[T]] in return a ..+ rhs }) } func ...+<T> (lhs: [[T]] , rhs: [[[T]]]) -> [[[T]]] where T:ImplementsInnerAddition { return rhs.map({(b: [[T]])->[[T]] in return lhs ..+ b }) } func .-<T> (lhs: [T], rhs: [T]) -> [T] where T:ImplementsInnerSubtraction { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: T,b: T)->T in return a - b }) } guard (lhs.count != 1) else { return rhs.map({(b: T)->T in return lhs[0] - b }) } guard (rhs.count != 1) else { return lhs.map({(a: T)->T in return a - rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func .-<T> (lhs: [T], rhs: T ) -> [T] where T:ImplementsInnerSubtraction { return lhs.map({(a: T)->T in return a - rhs }) } func .-<T> (lhs: T , rhs: [T]) -> [T] where T:ImplementsInnerSubtraction { return rhs.map({(b: T)->T in return lhs - b }) } func ..-<T> (lhs: [[T]], rhs: [[T]]) -> [[T]] where T:ImplementsInnerSubtraction { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: [T],b: [T])->[T] in return a .- b }) } guard (lhs.count != 1) else { return rhs.map({(b: [T])->[T] in return lhs[0] .- b }) } guard (rhs.count != 1) else { return lhs.map({(a: [T])->[T] in return a .- rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func ..-<T> (lhs: [[T]], rhs: [T] ) -> [[T]] where T:ImplementsInnerSubtraction { return lhs.map({(a: [T])->[T] in return a .- rhs }) } func ..-<T> (lhs: [T] , rhs: [[T]]) -> [[T]] where T:ImplementsInnerSubtraction { return rhs.map({(b: [T])->[T] in return lhs .- b }) } func ...-<T> (lhs: [[[T]]], rhs: [[[T]]]) -> [[[T]]] where T:ImplementsInnerSubtraction { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: [[T]],b: [[T]])->[[T]] in return a ..- b }) } guard (lhs.count != 1) else { return rhs.map({(b: [[T]])->[[T]] in return lhs[0] ..- b }) } guard (rhs.count != 1) else { return lhs.map({(a: [[T]])->[[T]] in return a ..- rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func ...-<T> (lhs: [[[T]]], rhs: [[T]] ) -> [[[T]]] where T:ImplementsInnerSubtraction { return lhs.map({(a: [[T]])->[[T]] in return a ..- rhs }) } func ...-<T> (lhs: [[T]] , rhs: [[[T]]]) -> [[[T]]] where T:ImplementsInnerSubtraction { return rhs.map({(b: [[T]])->[[T]] in return lhs ..- b }) } func .*<T> (lhs: [T], rhs: [T]) -> [T] where T:ImplementsInnerMultiplication { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: T,b: T)->T in return a * b }) } guard (lhs.count != 1) else { return rhs.map({(b: T)->T in return lhs[0] * b }) } guard (rhs.count != 1) else { return lhs.map({(a: T)->T in return a * rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func .*<T> (lhs: [T], rhs: T ) -> [T] where T:ImplementsInnerMultiplication { return lhs.map({(a: T)->T in return a * rhs }) } func .*<T> (lhs: T , rhs: [T]) -> [T] where T:ImplementsInnerMultiplication { return rhs.map({(b: T)->T in return lhs * b }) } func ..*<T> (lhs: [[T]], rhs: [[T]]) -> [[T]] where T:ImplementsInnerMultiplication { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: [T],b: [T])->[T] in return a .* b }) } guard (lhs.count != 1) else { return rhs.map({(b: [T])->[T] in return lhs[0] .* b }) } guard (rhs.count != 1) else { return lhs.map({(a: [T])->[T] in return a .* rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func ..*<T> (lhs: [[T]], rhs: [T] ) -> [[T]] where T:ImplementsInnerMultiplication { return lhs.map({(a: [T])->[T] in return a .* rhs }) } func ..*<T> (lhs: [T] , rhs: [[T]]) -> [[T]] where T:ImplementsInnerMultiplication { return rhs.map({(b: [T])->[T] in return lhs .* b }) } func ...*<T> (lhs: [[[T]]], rhs: [[[T]]]) -> [[[T]]] where T:ImplementsInnerMultiplication { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: [[T]],b: [[T]])->[[T]] in return a ..* b }) } guard (lhs.count != 1) else { return rhs.map({(b: [[T]])->[[T]] in return lhs[0] ..* b }) } guard (rhs.count != 1) else { return lhs.map({(a: [[T]])->[[T]] in return a ..* rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func ...*<T> (lhs: [[[T]]], rhs: [[T]] ) -> [[[T]]] where T:ImplementsInnerMultiplication { return lhs.map({(a: [[T]])->[[T]] in return a ..* rhs }) } func ...*<T> (lhs: [[T]] , rhs: [[[T]]]) -> [[[T]]] where T:ImplementsInnerMultiplication { return rhs.map({(b: [[T]])->[[T]] in return lhs ..* b }) } func ./<T> (lhs: [T], rhs: [T]) -> [T] where T:ImplementsInnerDivision { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: T,b: T)->T in return a / b }) } guard (lhs.count != 1) else { return rhs.map({(b: T)->T in return lhs[0] / b }) } guard (rhs.count != 1) else { return lhs.map({(a: T)->T in return a / rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func ./<T> (lhs: [T], rhs: T ) -> [T] where T:ImplementsInnerDivision { return lhs.map({(a: T)->T in return a / rhs }) } func ./<T> (lhs: T , rhs: [T]) -> [T] where T:ImplementsInnerDivision { return rhs.map({(b: T)->T in return lhs / b }) } func ../<T> (lhs: [[T]], rhs: [[T]]) -> [[T]] where T:ImplementsInnerDivision { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: [T],b: [T])->[T] in return a ./ b }) } guard (lhs.count != 1) else { return rhs.map({(b: [T])->[T] in return lhs[0] ./ b }) } guard (rhs.count != 1) else { return lhs.map({(a: [T])->[T] in return a ./ rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func ../<T> (lhs: [[T]], rhs: [T] ) -> [[T]] where T:ImplementsInnerDivision { return lhs.map({(a: [T])->[T] in return a ./ rhs }) } func ../<T> (lhs: [T] , rhs: [[T]]) -> [[T]] where T:ImplementsInnerDivision { return rhs.map({(b: [T])->[T] in return lhs ./ b }) } func .../<T> (lhs: [[[T]]], rhs: [[[T]]]) -> [[[T]]] where T:ImplementsInnerDivision { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: [[T]],b: [[T]])->[[T]] in return a ../ b }) } guard (lhs.count != 1) else { return rhs.map({(b: [[T]])->[[T]] in return lhs[0] ../ b }) } guard (rhs.count != 1) else { return lhs.map({(a: [[T]])->[[T]] in return a ../ rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func .../<T> (lhs: [[[T]]], rhs: [[T]] ) -> [[[T]]] where T:ImplementsInnerDivision { return lhs.map({(a: [[T]])->[[T]] in return a ../ rhs }) } func .../<T> (lhs: [[T]] , rhs: [[[T]]]) -> [[[T]]] where T:ImplementsInnerDivision { return rhs.map({(b: [[T]])->[[T]] in return lhs ../ b }) } func .+<T> (lhs: [T], rhs: [T]) -> [T] where T:FloatingPoint { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: T,b: T)->T in return a + b }) } guard (lhs.count != 1) else { return rhs.map({(b: T)->T in return lhs[0] + b }) } guard (rhs.count != 1) else { return lhs.map({(a: T)->T in return a + rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func .+<T> (lhs: [T], rhs: T ) -> [T] where T:FloatingPoint { return lhs.map({(a: T)->T in return a + rhs }) } func .+<T> (lhs: T , rhs: [T]) -> [T] where T:FloatingPoint { return rhs.map({(b: T)->T in return lhs + b }) } func ..+<T> (lhs: [[T]], rhs: [[T]]) -> [[T]] where T:FloatingPoint { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: [T],b: [T])->[T] in return a .+ b }) } guard (lhs.count != 1) else { return rhs.map({(b: [T])->[T] in return lhs[0] .+ b }) } guard (rhs.count != 1) else { return lhs.map({(a: [T])->[T] in return a .+ rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func ..+<T> (lhs: [[T]], rhs: [T] ) -> [[T]] where T:FloatingPoint { return lhs.map({(a: [T])->[T] in return a .+ rhs }) } func ..+<T> (lhs: [T] , rhs: [[T]]) -> [[T]] where T:FloatingPoint { return rhs.map({(b: [T])->[T] in return lhs .+ b }) } func ...+<T> (lhs: [[[T]]], rhs: [[[T]]]) -> [[[T]]] where T:FloatingPoint { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: [[T]],b: [[T]])->[[T]] in return a ..+ b }) } guard (lhs.count != 1) else { return rhs.map({(b: [[T]])->[[T]] in return lhs[0] ..+ b }) } guard (rhs.count != 1) else { return lhs.map({(a: [[T]])->[[T]] in return a ..+ rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func ...+<T> (lhs: [[[T]]], rhs: [[T]] ) -> [[[T]]] where T:FloatingPoint { return lhs.map({(a: [[T]])->[[T]] in return a ..+ rhs }) } func ...+<T> (lhs: [[T]] , rhs: [[[T]]]) -> [[[T]]] where T:FloatingPoint { return rhs.map({(b: [[T]])->[[T]] in return lhs ..+ b }) } func .-<T> (lhs: [T], rhs: [T]) -> [T] where T:FloatingPoint { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: T,b: T)->T in return a - b }) } guard (lhs.count != 1) else { return rhs.map({(b: T)->T in return lhs[0] - b }) } guard (rhs.count != 1) else { return lhs.map({(a: T)->T in return a - rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func .-<T> (lhs: [T], rhs: T ) -> [T] where T:FloatingPoint { return lhs.map({(a: T)->T in return a - rhs }) } func .-<T> (lhs: T , rhs: [T]) -> [T] where T:FloatingPoint { return rhs.map({(b: T)->T in return lhs - b }) } func ..-<T> (lhs: [[T]], rhs: [[T]]) -> [[T]] where T:FloatingPoint { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: [T],b: [T])->[T] in return a .- b }) } guard (lhs.count != 1) else { return rhs.map({(b: [T])->[T] in return lhs[0] .- b }) } guard (rhs.count != 1) else { return lhs.map({(a: [T])->[T] in return a .- rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func ..-<T> (lhs: [[T]], rhs: [T] ) -> [[T]] where T:FloatingPoint { return lhs.map({(a: [T])->[T] in return a .- rhs }) } func ..-<T> (lhs: [T] , rhs: [[T]]) -> [[T]] where T:FloatingPoint { return rhs.map({(b: [T])->[T] in return lhs .- b }) } func ...-<T> (lhs: [[[T]]], rhs: [[[T]]]) -> [[[T]]] where T:FloatingPoint { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: [[T]],b: [[T]])->[[T]] in return a ..- b }) } guard (lhs.count != 1) else { return rhs.map({(b: [[T]])->[[T]] in return lhs[0] ..- b }) } guard (rhs.count != 1) else { return lhs.map({(a: [[T]])->[[T]] in return a ..- rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func ...-<T> (lhs: [[[T]]], rhs: [[T]] ) -> [[[T]]] where T:FloatingPoint { return lhs.map({(a: [[T]])->[[T]] in return a ..- rhs }) } func ...-<T> (lhs: [[T]] , rhs: [[[T]]]) -> [[[T]]] where T:FloatingPoint { return rhs.map({(b: [[T]])->[[T]] in return lhs ..- b }) } func .*<T> (lhs: [T], rhs: [T]) -> [T] where T:FloatingPoint { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: T,b: T)->T in return a * b }) } guard (lhs.count != 1) else { return rhs.map({(b: T)->T in return lhs[0] * b }) } guard (rhs.count != 1) else { return lhs.map({(a: T)->T in return a * rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func .*<T> (lhs: [T], rhs: T ) -> [T] where T:FloatingPoint { return lhs.map({(a: T)->T in return a * rhs }) } func .*<T> (lhs: T , rhs: [T]) -> [T] where T:FloatingPoint { return rhs.map({(b: T)->T in return lhs * b }) } func ..*<T> (lhs: [[T]], rhs: [[T]]) -> [[T]] where T:FloatingPoint { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: [T],b: [T])->[T] in return a .* b }) } guard (lhs.count != 1) else { return rhs.map({(b: [T])->[T] in return lhs[0] .* b }) } guard (rhs.count != 1) else { return lhs.map({(a: [T])->[T] in return a .* rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func ..*<T> (lhs: [[T]], rhs: [T] ) -> [[T]] where T:FloatingPoint { return lhs.map({(a: [T])->[T] in return a .* rhs }) } func ..*<T> (lhs: [T] , rhs: [[T]]) -> [[T]] where T:FloatingPoint { return rhs.map({(b: [T])->[T] in return lhs .* b }) } func ...*<T> (lhs: [[[T]]], rhs: [[[T]]]) -> [[[T]]] where T:FloatingPoint { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: [[T]],b: [[T]])->[[T]] in return a ..* b }) } guard (lhs.count != 1) else { return rhs.map({(b: [[T]])->[[T]] in return lhs[0] ..* b }) } guard (rhs.count != 1) else { return lhs.map({(a: [[T]])->[[T]] in return a ..* rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func ...*<T> (lhs: [[[T]]], rhs: [[T]] ) -> [[[T]]] where T:FloatingPoint { return lhs.map({(a: [[T]])->[[T]] in return a ..* rhs }) } func ...*<T> (lhs: [[T]] , rhs: [[[T]]]) -> [[[T]]] where T:FloatingPoint { return rhs.map({(b: [[T]])->[[T]] in return lhs ..* b }) } func ./<T> (lhs: [T], rhs: [T]) -> [T] where T:FloatingPoint { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: T,b: T)->T in return a / b }) } guard (lhs.count != 1) else { return rhs.map({(b: T)->T in return lhs[0] / b }) } guard (rhs.count != 1) else { return lhs.map({(a: T)->T in return a / rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func ./<T> (lhs: [T], rhs: T ) -> [T] where T:FloatingPoint { return lhs.map({(a: T)->T in return a / rhs }) } func ./<T> (lhs: T , rhs: [T]) -> [T] where T:FloatingPoint { return rhs.map({(b: T)->T in return lhs / b }) } func ../<T> (lhs: [[T]], rhs: [[T]]) -> [[T]] where T:FloatingPoint { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: [T],b: [T])->[T] in return a ./ b }) } guard (lhs.count != 1) else { return rhs.map({(b: [T])->[T] in return lhs[0] ./ b }) } guard (rhs.count != 1) else { return lhs.map({(a: [T])->[T] in return a ./ rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func ../<T> (lhs: [[T]], rhs: [T] ) -> [[T]] where T:FloatingPoint { return lhs.map({(a: [T])->[T] in return a ./ rhs }) } func ../<T> (lhs: [T] , rhs: [[T]]) -> [[T]] where T:FloatingPoint { return rhs.map({(b: [T])->[T] in return lhs ./ b }) } func .../<T> (lhs: [[[T]]], rhs: [[[T]]]) -> [[[T]]] where T:FloatingPoint { guard (lhs.count != rhs.count) else { return zip(lhs,rhs).map({(a: [[T]],b: [[T]])->[[T]] in return a ../ b }) } guard (lhs.count != 1) else { return rhs.map({(b: [[T]])->[[T]] in return lhs[0] ../ b }) } guard (rhs.count != 1) else { return lhs.map({(a: [[T]])->[[T]] in return a ../ rhs[0] }) } assert(false,"Element-wise operation can only be applied to arrays of same size or alternatively if one of the array is of size 1",file:#file,line:#line) } func .../<T> (lhs: [[[T]]], rhs: [[T]] ) -> [[[T]]] where T:FloatingPoint { return lhs.map({(a: [[T]])->[[T]] in return a ../ rhs }) } func .../<T> (lhs: [[T]] , rhs: [[[T]]]) -> [[[T]]] where T:FloatingPoint { return rhs.map({(b: [[T]])->[[T]] in return lhs ../ b }) } _______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution