Swift数字泛型?

5
我有一个函数,将从两个数字计算出斜边长度。
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
}

我希望使用泛型,这样就不用分别使用Int、Float和Double来制作3个副本了。

但是这并没有起作用,我认为泛型真的很难处理,请帮助我 :)

4个回答

6

Swift泛型不像C++模板。

在C++中,你可以随意使用参数化类型,只有当编译器尝试使用一些不支持模板操作的类型来实例化模板时,才会出错。

而在Swift中,泛型构造只能以在首次解析泛型构造时已知为有效的方式使用参数化类型。你需要使用协议来限制参数化类型,以指定这些“已知为有效的方式”。

你不能使用泛型类型的参数调用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

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

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

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函数一样存在问题:使用+运算符并不是完全通用的。虽然Swift有一些通用的定义(与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

1
我明白了。我不知道 hypot() 哈哈,感觉自己好傻 =S,所以也许泛型并不像我想的那么高级 :( - Arbitur
Float80在sqrt或pow中未定义,您需要编写自己的代码或使用类库,如boost。 - μολὼν.λαβέ

6

使用Swift 5,根据您的需求,您可以选择以下方法之一来解决问题。


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

Apple开发者文档中关于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有以下声明:

具有支持加法和减法的值类型。

下面是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 的声明如下:

具有支持乘法的值类型。

下面的 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协议。
苹果开发者文档包含所有数字协议的专门页面:数字协议

请注意,已经可用相当长一段时间的hypot函数可以与CGFloat一起使用。 - Sulthan

2

我认为这里是你需要的:

你需要明确地创建一个新协议,并将你想要的类型(Int,Float,Double)扩展到符合该协议。然后在你的泛型声明中,你可以这样做: func addition<T: protocolJustCreated>(nr1: T, nr2: T) -> T {}

阅读我链接的答案以获取更完整的答案。这里不需要重复。


0

Sqrt()和pow()函数都将其参数指定为double或float。为了实现您使用此一函数处理Int、Float和Double的目标,您还需要使sqrt()和pow()函数成为范型函数。


如果您重新加载问题,我添加了一个更简单的情况。在那个addition()函数的返回行上,我会收到一个错误消息:“无法使用类型(T,T)的参数调用+”。 - Arbitur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接