Swift 数字仿制药?

Swift number generics?

我有这个函数可以根据 2 个数字计算斜边

func hypotenusa<T>(nr1: T, nr2: T) -> T {
    return sqrt( pow(nr1, 2) + pow(nr2, 2) )
}

// v Simpler situation v

func addition<T>(nr1: T, nr2: T) -> T {
    return nr1 + nr2
}

我想使用泛型,所以我不必制作 3 个分别使用 Int、Float、Double 的副本

但这行不通,我认为泛型真的很难用,请帮助我:)

Sqrt() 和 pow() 都将它们的参数指定为 double 或 float。为了实现将这一函数用于 Int、Float 和 Double 的目标,您还需要对 sqrt() 和 pow() 函数进行泛型化。

Swift 泛型不像 C++ 模板。

在 C++ 中,您可以随心所欲地尝试使用参数化类型,并且在编译器尝试使用某种不支持您的模板尝试执行的操作的类型来实例化模板之前,这不是错误。

在 Swift 中,泛型构造只能以在首次解析泛型构造时已知有效的方式使用参数化类型。您可以通过使用协议约束参数化类型来指定这些 "ways known to be valid"。

您不能使用泛型参数调用 sqrtpow,因为这些函数本身不是泛型的。它们各有两个定义:

func pow(_: Double, _: Double) -> Double
func pow(lhs: Float, rhs: Float) -> Float
func sqrt(x: Double) -> Double
func sqrt(x: Float) -> Float

您可以编写 hypotenusa:

的特定类型版本
func hypotenusa(a: Float, b: Float) -> Float
func hypotenusa(a: Double, b: Double) -> Double
func hypotenusa(a: CGFloat, b: CGFloat) -> CGFloat

我完全不确定你为什么要创建一个 Int 版本,因为很少有直角三角形有整数斜边。

无论如何,你根本不需要定义FloatDouble版本,因为标准库已经提供了定义在Float上的hypot函数和 Double:

func hypot(_: Double, _: Double) -> Double
func hypot(lhs: Float, rhs: Float) -> Float

您可以为 CGFloat 创建另一个覆盖:

func hypot(l: CGFloat, r: CGFloat) -> CGFloat {
    return hypot(Double(l), Double(r))
}

至于您的 addition 函数,它与您的 hypotenusa 函数有同样的问题:+ 运算符没有完全通用地定义。它有一些通用定义(不像 sqrtpow),但那些只涵盖整数类型(参见 IntegerArithmeticType)。没有涵盖浮点类型的 + 的通用定义。 Swift 定义了具有显式类型的 + 的所有这些版本:

func +(lhs: Float, rhs: Float) -> Float
func +<T>(lhs: Int, rhs: UnsafePointer<T>) -> UnsafePointer<T>
func +<T>(lhs: UnsafePointer<T>, rhs: Int) -> UnsafePointer<T>
func +(lhs: Int, rhs: Int) -> Int
func +(lhs: UInt, rhs: UInt) -> UInt
func +(lhs: Int64, rhs: Int64) -> Int64
func +(lhs: UInt64, rhs: UInt64) -> UInt64
func +<T>(lhs: Int, rhs: UnsafeMutablePointer<T>) -> UnsafeMutablePointer<T>
func +<T>(lhs: UnsafeMutablePointer<T>, rhs: Int) -> UnsafeMutablePointer<T>
func +(lhs: Int32, rhs: Int32) -> Int32
func +(lhs: UInt32, rhs: UInt32) -> UInt32
func +(lhs: Int16, rhs: Int16) -> Int16
func +(lhs: UInt16, rhs: UInt16) -> UInt16
func +(lhs: Int8, rhs: Int8) -> Int8
func +(lhs: UInt8, rhs: UInt8) -> UInt8
func +(lhs: Double, rhs: Double) -> Double
func +(lhs: String, rhs: String) -> String
func +(lhs: Float80, rhs: Float80) -> Float80

我认为 this 是您需要的:

您需要显式创建一个新协议并扩展您想要的类型(Int、Float、Double)以符合该协议。比在你的通用声明中你做的 func addition<T: protocolJustCreated>(nr1: T, nr2: T) -> T {}

阅读我链接的答案以获得更完整的答案。此处无需赘述。

同 Swift 5、根据您的需要,您可以选择以下方式之一来解决您的问题。


#1。使用 FloatingPoint 协议作为参数泛型约束

Apple Developer Documentation for FloatingPoint 显示以下 hypotenuse 函数实现作为 FloatingPoint 用法的示例:

func hypotenuse<T: FloatingPoint>(_ a: T, _ b: T) -> T {
    return (a * a + b * b).squareRoot()
}

let (dx, dy) = (3.0, 4.0)
let result = hypotenuse(dx, dy)
print(result) // prints: 5.0

#2。使用 AdditiveArithmetic 协议作为参数泛型约束

AdditiveArithmetic 具有以下声明:

A type with values that support addition and subtraction.

下面的 Playground 示例代码显示了 AdditiveArithmetic 作为函数参数通用约束的可能用法:

func addition<T: AdditiveArithmetic>(a: T, b: T) -> T {
    return a + b
}

let result = addition(a: 3, b: 4)
print(result) // prints: 7

#3。使用 Numeric 协议作为参数泛型约束

Numeric 具有以下声明:

A type with values that support multiplication.

下面的 Playground 示例代码显示了 Numeric 作为函数参数通用约束的可能用法:

func multiply<T: Numeric>(a: T, b: T, c: T) -> T {
    return a * b * c
}

let result = multiply(a: 3, b: 4, c: 5)
print(result) // prints: 60

请注意,Numeric 协议继承自 AdditiveArithmetic 协议。


Apple 开发者文档包含所有数字协议的专用页面:Numeric Protocols