Swift泛型数值类型和数学

6

我正在努力理解Swift泛型的内部细节,并编写一些常见数学函数。我试图实现一个mod函数,但不确定使用泛型使其正常工作的最佳方法。

下面是我的mod函数:

func mod<N: NumericType, I: IntegerType>(_ x: N, _ y: I) -> N {
    return x - y * floor(x/y)
}

但是我遇到了这个错误:
error: binary operator '/' cannot be applied to operands of type 'N' and 'I'
    return x - y * floor(x/y)

这是我的 NumericType 声明,用于十进制和整数类型的数字:

protocol NumericType: Comparable {
    static func +(lhs: Self, rhs: Self) -> Self
    static func -(lhs: Self, rhs: Self) -> Self
    static func *(lhs: Self, rhs: Self) -> Self
    static func /(lhs: Self, rhs: Self) -> Self
    static func %(lhs: Self, rhs: Self) -> Self
}

protocol DecimalType: NumericType {
    init(_ v: Double)
}

protocol IntegerType: NumericType {
    init(_ v: Int)
}

extension CGFloat : DecimalType { }
extension Double  : DecimalType { }
extension Float   : DecimalType { }

extension Int     : IntegerType { }
extension Int8    : IntegerType { }
extension Int16   : IntegerType { }
extension Int32   : IntegerType { }
extension Int64   : IntegerType { }
extension UInt    : IntegerType { }
extension UInt8   : IntegerType { }
extension UInt16  : IntegerType { }
extension UInt32  : IntegerType { }
extension UInt64  : IntegerType { }

1
请注意,从Swift 3开始,所有整数类型都符合“Integer”协议,所有浮点类型都符合“FloatingPoint”协议。这两个协议已经定义了基本的算术运算符,如+、-、*、/。也许这有助于简化代码。 - Martin R
@MartinR 这个方法很管用!大大简化了我的代码,不再需要自定义协议 https://twitter.com/iamkgn/status/773778299487002624 谢谢! - keegan3d
2个回答

7
从Swift 3开始,所有浮点类型都符合“FloatingPoint”协议,所有整数类型都符合“Integer”协议。两个协议都定义了基本算术运算,如+,-,*,/。此外,“floor()”函数也定义为针对“FloatingPoint”参数。
因此,在您的情况下,我会定义两个实现,一个用于整数,另一个用于浮点值:
func mod<N: Integer>(_ x: N, _ y: N) -> N {
    return x - y * (x/y) // or just: return x % y
}

func mod<N: FloatingPoint>(_ x: N, _ y: N) -> N {
    return x - y * floor(x/y)
}

FloatingPoint还有一个truncatingRemainder方法, a.truncatingRemainder(b)是整数的a%b的“浮点等价物”。如果两个运算数都具有相同的符号,则它会给出与您的mod函数相同的结果。


2
static func /(lhs: Self, rhs: Self) -> Self

这意味着lhsrhs必须是相同类型。在x / y中,x的类型是N,而y的类型是I。因此,xy不同的类型,所以x / y无法运行。
您需要先将yI转换为N
let numericY = N(integer: y)
return x - numericY * floor(x / numericY)

这意味着你的NumericType需要能够从IntegerType初始化,除了具有+-*/%操作符。
protocol NumericType: Comparable {
    init<I: IntegerType>(integer: I)
    ...
}

此外,应该存在floor<N:NumericType>(n:N) -> N以使整个表达式编译。


太棒了,这真的很有帮助,但是当我将init添加到NumericType协议中时,所有扩展都需要符合,有没有什么解决方法?error: type 'CGFloat' does not conform to protocol 'NumericType' - keegan3d
@keegan3d 很不幸,您需要自己实现该方法。您可以尝试使用 self.init(integer.toIntMax()) - kennytm

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