[swift-evolution] Basic element-wise operator set for Arrays, Arrays of Arrays, etc.

Xiaodi Wu xiaodi.wu at gmail.com
Fri Feb 17 12:01:35 CST 2017


If you're simply looking for elementwise multiply without performance
requirements, map(*) is a very succinct spelling.

Performant implementations for these operations like you have in Matlab
rely on special math libraries. Apple platforms have Accelerate that makes
this possible, and other implementations of BLAS/LAPACK do the same for
Linux and Windows platforms.

There has been talk on this list of writing Swifty wrappers for such
libraries. The core team has said that the way to get such facilities into
Swift corelibs is to write your own library, get broad adoption, then
propose its acceptance here. Currently, several libraries like Surge and
Upsurge offer vectorized wrappers in Swifty syntax for Apple platforms; it
would be interesting to explore whether the same can be done in a
cross-platform way.

But simply adding sugar to the standard library will not give you the
results you're looking for (by which I mean, the performance will be
unacceptable), and there's no point in providing sugar for something that
doesn't work like the operator implies (Matlab's elementwise operators
offer _great_ performance).




On Fri, Feb 17, 2017 at 11:46 Nicolas Fezans via swift-evolution <
swift-evolution at swift.org> wrote:

> 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 at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170217/b29d848b/attachment.html>


More information about the swift-evolution mailing list