使用协议切换通用数字函数

时间:2016-02-25 22:44:09

标签: swift generics protocols swift-extensions

这对双打和花车很有用。但是得到其他数字如Ints也很难实现。

public protocol TemperatureConvertable: FloatLiteralConvertible, CustomStringConvertible {
    func +(lhs: Self, rhs: Self) -> Self
    func -(lhs: Self, rhs: Self) -> Self
    func *(lhs: Self, rhs: Self) -> Self
    func /(lhs: Self, rhs: Self) -> Self
}

extension Double: TemperatureConvertable {}
extension Float: TemperatureConvertable {}

public func fahrenheitToCelsius<T: TemperatureConvertable>(fahrenheit: T) -> T {
    // (°F − 32) ÷ 1.8 =°C
    return (fahrenheit - 32.0) / 1.8
}

public func celsiusToFahrenheit<T: TemperatureConvertable>(celsius: T) -> T {
    // (°C × 1.8) + 32 =°F
    return (celsius * 1.8) + 32.0
}

根据我的发现,没有NumberLiteralConvertible或类似的。只是像其他人一样创建Numeric协议会使算法失败,因为类型不匹配。理想情况下,我喜欢这样的更通用的东西,但是我得到Binary operator '-' cannot be applied to operands of type 'T' and 'Double'的实际数学线为华氏度 - 32.0,因为lhs和rhs都不是Self。

public protocol TemperatureConvertable: CustomStringConvertible {
    func +(lhs: Self, rhs: Self) -> Self
    func -(lhs: Self, rhs: Self) -> Self
    func *(lhs: Self, rhs: Self) -> Self
    func /(lhs: Self, rhs: Self) -> Self
}

extension Int: TemperatureConvertable {}
extension Double: TemperatureConvertable {}
extension Float: TemperatureConvertable {}

public func fahrenheitToCelsius<T: TemperatureConvertable>(fahrenheit: T) -> T {
    // (°F − 32) ÷ 1.8 =°C
    return (fahrenheit - 32.0) / 1.8
}

public func celsiusToFahrenheit<T: TemperatureConvertable>(celsius: T) -> T {
    // (°C × 1.8) + 32 =°F
    return (celsius * 1.8) + 32.0
}

1 个答案:

答案 0 :(得分:2)

您需要一种方法从other-modules创建TemperatureConvertable,反之亦然。

因此,您的函数将能够在内部使用Double来执行操作。最后将结果转换为Double(s)以获得输出。

第1步

T

现在,我们可以将您的扩展程序更新为public protocol TemperatureConvertible: CustomStringConvertible { // this part is not needed // 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(_ other: Double) var double: Double { get } } DoubleFloat

  

您不需要添加我们上面声明的初始值设定项,因为这些类型已经拥有它。

Int

第2步

现在你可以像这样重写你的功能

extension Int: TemperatureConvertible {
    public var double: Double { return Double(self) }
}
extension Double: TemperatureConvertible {
    public var double: Double { return self }
}
extension Float: TemperatureConvertible {
    public var double: Double { return Double(self) }
}

测试

public func fahrenheitToCelsius<T: TemperatureConvertible>(fahrenheit: T) -> T {
    // (°F − 32) ÷ 1.8 =°C
    let celsius = (fahrenheit.double - 32.0) / 1.8
    return T(celsius)
}

public func celsiusToFahrenheit<T: TemperatureConvertible>(celsius: T) -> T {
    // (°C × 1.8) + 32 =°F
    let fahrenheit = (celsius.double * 1.8) + 32.0
    return T(fahrenheit)
}

提示

为了便于阅读并与Cocoa框架保持一致,我建议你重命名你的函数

let celsius: Int = 20
let fahrenheit = celsiusToFahrenheit(celsius)
//  ^ it's an Int