Reputation: 900
I have a problem with a class using a generic type T for an internal array which can be an Int
or Double
. The function average
shall calcucualte the average values of all Int
or Double
values in the array.
class MathStatistics<T: Comparable> {
var numbers = [T]()
func average() -> Double? {
if numbers.count == 0 {
return nil
}
var sum:T
for value in numbers {
sum = sum + value
}
return (sum / numbers.count)
}
}
Xcode reports an error in line the following lines:
sum = sum + value
Binary operator '+' cannot apply two T operants
return (sum / numbers.count)
Couldn't find an overload for '/' that accepts the supplied arguments
Upvotes: 1
Views: 1318
Reputation: 900
It seems that there is a bug in SWIFT when I want to use two protocols on a generic type T.
The following code works perfectly.
protocol DoubleConvertible {
var doubleValue : Double { get }
}
protocol NumericType: Equatable, Comparable, DoubleConvertible {
func +(lhs: Self, rhs: Self) -> Self
func -(lhs: Self, rhs: Self) -> Self
func *(lhs: Self, rhs: Self) -> Self
func /(lhs: Self, rhs: Self) -> Self
func %(lhs: Self, rhs: Self) -> Self
init(_ v: Int)
init(_ v: Double)
}
extension Double : NumericType {
var doubleValue : Double { return self }
}
extension Int : NumericType {
var doubleValue : Double { return Double(self) }
}
class MathStatistics<T: NumericType> {
var numbers = [T]()
func average() -> Double? {
if numbers.count == 0 {
return nil
}
let sum = numbers.reduce(T(0)) { $0 + $1 }
return sum.doubleValue / numbers.count.doubleValue
}
}
This version produces the error T does not have a member named doubleValue
in line return sum.doubleValue / numbers.count.doubleValue
protocol NumericType: Equatable, Comparable {
func +(lhs: Self, rhs: Self) -> Self
func -(lhs: Self, rhs: Self) -> Self
func *(lhs: Self, rhs: Self) -> Self
func /(lhs: Self, rhs: Self) -> Self
func %(lhs: Self, rhs: Self) -> Self
init(_ v: Int)
init(_ v: Double)
}
protocol DoubleConvertible {
var doubleValue : Double { get }
}
extension Double : DoubleConvertible {
var doubleValue : Double { return self }
}
extension Int : DoubleConvertible {
var doubleValue : Double { return Double(self) }
}
class MathStatistics<T: NumericType, DoubleConvertible > {
var numbers = [T]()
func average() -> Double? {
if numbers.count == 0 {
return nil
}
let sum = numbers.reduce(T(0)) { $0 + $1 }
return sum.doubleValue / numbers.count.doubleValue // error T does not have a member named doubleValue
}
}
Upvotes: 0
Reputation: 539975
If your intention is to compute the average in floating point arithmetic
then you have to define a protocol which describes
all types which can be converted to Double
:
protocol DoubleConvertible {
var doubleValue : Double { get }
}
extension Double : DoubleConvertible {
var doubleValue : Double { return self }
}
extension Int : DoubleConvertible {
var doubleValue : Double { return Double(self) }
}
Then your class would be:
class MathStatistics<T: DoubleConvertible> {
var numbers = [T]()
func average() -> Double? {
if numbers.count == 0 {
return nil
}
var sum = 0.0
for value in numbers {
sum = sum + value.doubleValue
}
return (sum / numbers.count.doubleValue)
}
}
Example:
let stats = MathStatistics<Int>()
stats.numbers = [3, 5,2]
println(stats.average()) // Optional(3.33333333333333)
Upvotes: 2
Reputation: 22939
For this you need to create a new protocol
that lets Swift know any instance of T
can have numeric operators performed on it, for example:
protocol NumericType: Equatable, Comparable {
func +(lhs: Self, rhs: Self) -> Self
func -(lhs: Self, rhs: Self) -> Self
func *(lhs: Self, rhs: Self) -> Self
func /(lhs: Self, rhs: Self) -> Self
func %(lhs: Self, rhs: Self) -> Self
init(_ v: Int)
}
extension Double : NumericType {}
extension Int : NumericType {}
Now when you define your MathStatistics
class:
class MathStatistics<T: NumericType> {
var numbers = [T]()
func average() -> T? {
if numbers.count == 0 {
return nil
}
let sum = numbers.reduce(T(0)) { $0 + $1 }
return sum / T(numbers.count)
}
}
Now you can use MathsStatistics
like so:
let stats = MathStatistics<Int>()
stats.numbers = [1, 3, 5]
println(stats.average()) // Prints: Optional(3)
Upvotes: 3