我试图通过将通用类型设置为Int
来在Swift中编写一个通用函数,该通用函数可以接受Float
,Double
,<T: Numeric>
等任何数字。所以,
func doSomething<T: Numeric>(with foo: T, and bar: T) -> T {
// body
}
大部分身体都可以用于任何Numeric
类型,但是在此过程中,我需要找到其余部分...这意味着对于FloatingPoint
类型与{{ 1}}类型。
当Integer
是T
时,这意味着使用模运算符:
Int
但是,当let remainder = foo % bar
是T
或Double
时,模运算符不起作用,我需要使用Float
方法:
truncatingRemainder(dividingBy:)
我在努力的地方是找到一种方法来筛选这些。从理论上讲,我应该能够执行以下操作:
let remainder = foo.truncatingRemainder(dividingBy: bar)
这当然会导致此错误,因为var remainder: T
if T.self as FloatingPoint { // <- This doesn't work
remainder = foo.truncatingRemainder(dividingBy: bar)
} else {
remainder = foo % bar
}
具有关联的类型:
FloatingPoint
我理解此错误...本质上,error: protocol 'FloatingPoint' can only be used as a generic constraint because it has Self or associated type requirements
是通用的,具有尚通用的关联类型供采用的类型定义。
但是,我想知道最好的方法来有条件地运行选择的代码块,这些代码块仅适用于比通用参数(FloatingPoint
定义的更狭窄定义的协议。
特别是,有一种方法可以定义一个通用函数来处理所有T
类型,然后通过Numeric
与内部的FloatingPoint
类型进行区分。
答案 0 :(得分:1)
这里有几个问题。
除非我误读了Numeric的文档,否则矩阵类型可以合理地符合Numeric。如果将矩阵传递到函数中,则您将没有真正的方法来计算余数,因为这不是一个很好定义的概念。因此,不应在所有数值类型上都定义您的函数。解决方案是定义一个新协议,该协议描述具有明确定义的余数的类型。不幸的是,正如亚历山大在评论中指出的那样...
此限制有多种技术原因,主要集中在类型将以多种方式符合协议的困难上。
我认为您有两个合理的解决方案。
A。进行doSomething
的两个不同的重载,一个重载T: FloatingPoint
,另一个重载T: BinaryInteger
。如果这些实现之间共享的代码太多,则可以将doSomething
重构为多个函数,其中大多数将在所有Numeric
上定义。
B。引入一个新协议RemainderArithmetic: Numeric
,该协议描述了所需的操作。然后,为要使用的所有特定类型写明确的一致性,例如extension Double: RemainderArithmetic
和extension UInt: RemainderArithmetic
。
这两种解决方案都没有特别吸引人,但我认为它们具有一个关键优势:这两种解决方案都清楚说明了您期望的类型的特殊语义。除了BinaryInteger
或FloatingPoint
之外的其他类型,您还没有真正预期,所以您不应该接受其他类型。 wikipedia page for mod中描述的多种行为证明了余数的语义非常棘手。因此,跨整数和浮点数以相同的方式定义函数可能不是很自然。如果确定这是您要执行的操作,那么这两种解决方案都将使您对期望的类型做出明确的假设。
如果您对这两种解决方案都不满意,并且可以提供有关您到底想做什么的更多详细信息,我们也许可以找到其他东西。
答案 1 :(得分:1)
这听起来不像是泛型的好用例。您会注意到,+
之类的运算符在Swift中不是通用的。他们使用超载,您也应该这样做。