Morpheus78
Morpheus78

Reputation: 900

Usage of arithmetic operants (+, -, /, *) with generic types

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

Answers (3)

Morpheus78
Morpheus78

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 doubleValuein 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

Martin R
Martin R

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

ABakerSmith
ABakerSmith

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 {}

Source: What protocol should be adopted by a Type for a generic function to take any number type as an argument in Swift?

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

Related Questions