[swift-evolution] Basic element-wise operator set for Arrays, Arrays of Arrays, etc.
Nicolas Fezans
nicolas.fezans at gmail.com
Fri Feb 17 11:44:59 CST 2017
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 }) }
More information about the swift-evolution
mailing list