我使用Swift泛型键入了两个版本的离散卷积算法。整数版本有效。但是浮点版本存在乘法问题:
import Foundation
import Swift
func linconv<T: IntegerType>(signal_A signal_A: [T], signal_B: [T]) -> [T]? {
// guard
guard signal_A.isEmpty == false && signal_B.isEmpty == false else {
return nil
}
// reverse at least one of the arrays
//let signal_A_reversed = Array(signal_A.reverse())
// size of new array
let N = signal_A.count + signal_B.count - 1
// new array for result
var resultSignal = [T](count: N, repeatedValue: 0)
for n in 0..<N {
for j in 0...n {
if j < signal_B.count && (n - j) < signal_A.count {
resultSignal[n] += signal_B[j] * signal_A[n - j]
}
}
}
return resultSignal
}
func linconv<T: FloatingPointType>(signal_A signal_A: [T], signal_B: [T]) -> [T]? {
// guard
guard signal_A.isEmpty == false && signal_B.isEmpty == false else {
return nil
}
// reverse at least one of the arrays
//let signal_A_reversed = Array(signal_A.reverse())
// size of new array
let N = signal_A.count + signal_B.count - 1
// new array for result
var resultSignal = [T](count: N, repeatedValue: T(0))
for n in 0..<N {
for j in 0...n {
if j < signal_B.count && (n - j) < signal_A.count {
resultSignal[n] += signal_B[j] * signal_A[n - j] // compiler says error here!
}
}
}
return resultSignal
}
对于FloatingPointType版本,编译器说“二进制运算符'*'不能应用于两个'T'操作数”。但是,它不会在IntegerType版本上执行此操作。为什么呢?
答案 0 :(得分:5)
<{em> FloatingPointType
和Double
类型确实采用了Float
协议,但相反,协议由于某种原因不包括用于运算符方法的蓝图,例如(在您的情况下),*
二元运算符或+=
赋值运算符。请注意,仅仅因为某些已知类型采用协议的重要性,该协议本身不一定包含我们对采用它的那些类型所期望的所有蓝图。
另一方面,IntegerType
协议包括运算符方法的蓝图。
因此,符合协议T
的通用FloatingPointType
不一定(在swift眼中)可以乘法等等,因为协议不包含此类操作的蓝图。如果我们查看standard library reference for FloatingPointType,我们会发现它似乎仅由 Double
,Float
(和CGFloat
)采用。我们知道所有这三种类型都可以通过我们的常规操作员自行运行,所以嘿,为什么我们不能在符合FloatingPointType
的通用符号上使用这些运算符?同样,符合协议的类型集合实际上无法深入了解协议包含的蓝图。
作为一个例子,请查看以下协议中某些基本类型的扩展以符合它
protocol ReallyLotsOfAdditionalStuff {}
extension Int : ReallyLotsOfAdditionalStuff {}
extension Double : ReallyLotsOfAdditionalStuff {}
此虚拟协议的库参考将列出仅Int
和Double
采用类型的类型。相反,如果我们不小心,我们可以期望符合协议ReallyLotsOfAdditionalStuff
的泛型至少可以说是可添加的(除了许多额外的东西),但自然,情况并非如此。
无论如何,您可以自行修复此问题,方法是创建一个新协议,作为T
函数中通用FloatingPointType
的附加类型约束添加。
protocol MyNecessaryFloatingPointTypeOperations {
func *(lhs: Self, rhs: Self) -> Self
func += (inout lhs: Self, rhs: Self)
// ... other necessary floating point operator blueprints ...
}
extension Float: MyNecessaryFloatingPointTypeOperations {}
extension Double: MyNecessaryFloatingPointTypeOperations {}
// Example: only type constraint to FloatingPointType
func errorFloatingPointType<T: FloatingPointType> (a: T, b: T) -> T {
return a * b // Error: binary operator '*' cannot be applied to two 'T' operands
}
// Example: additional type constraint to your custom protocol
func noErrorFloatingPointType<T: protocol<FloatingPointType, MyNecessaryFloatingPointTypeOperations>> (a: T, b: T) -> T {
return a * b // ok!
}
因此,要修复您的FloatingPointType
,请在函数标题中将自定义协议添加为T
的附加类型约束:
func linconv<T: protocol<FloatingPointType, MyNecessaryFloatingPointTypeOperations>>(signal_A: [T], signal_B: [T]) -> [T]? {
// ...
}
或者,让您自己的协议继承FloatingPointType
并添加您的&#34;子协议所需的任何其他方法&#34;,例如:
protocol ImprovedFloatingPointType : FloatingPointType {
func *(lhs: Self, rhs: Self) -> Self
func += (inout lhs: Self, rhs: Self)
// ... other necessary integer and floating point blueprints
}
extension Float: ImprovedFloatingPointType {}
extension Double: ImprovedFloatingPointType {}
func linconv<T: ImprovedFloatingPointType>(signal_A: [T], signal_B: [T]) -> [T]? {
// ...
}
最后,我们可能会问,我们是否首先需要FloatingPointType
协议(即使作为我们自定义协议的父协议)?如果我们只想为处理两个快速浮点类型Double
和Float
制作泛型,那么我们也可以只将协议MyNecessaryFloatingPointTypeOperations
作为类型约束应用于我们的泛型:< / p>
func myFloatingPointGenericFunction<T: MyNecessaryFloatingPointTypeOperations> (a: T, b: T) -> T {
// ...
return a * b
}
正如您可能已经知道的那样,我们需要通用符合单个蓝图的FloatingPointType
协议:确定我们的泛型函数,我们可以使用整数初始化器初始化T
个实例,即{{ 1}}。例如,在你的职能中:
init(_ value: Int)
但是,如果这是我们在// new array for result
var resultSignal = [T](count: N, repeatedValue: T(0)) // <--
协议中使用的唯一蓝图,我们不妨将其作为蓝图添加到我们自己的协议中,并将通用类型约束移除到{{ 1}}完全。
FloatingPointType
有了这个,我们意识到我们并不需要为整数类型和浮点类型分别使用两个单独的泛型。因为我们(对于你的例子)只需要 1。一个by-int-initializer,2. *二元运算符,和3. + =赋值运算符,我们可以为符合这些蓝色标记的所有类型构造一个泛型作为类型约束,并扩展我们的类型希望通过此协议涵盖我们的通用。
FloatingPointType
然而,在这里,我们看到了使用现有protocol MyNecessaryFloatingPointTypeOperations {
func *(lhs: Self, rhs: Self) -> Self
func += (inout lhs: Self, rhs: Self)
init(_ value: Int)
// ... other necessary floating point blueprints
}
extension Float: MyNecessaryFloatingPointTypeOperations {}
extension Double: MyNecessaryFloatingPointTypeOperations {}
func myFloatingPointGenericFunction<T: MyNecessaryFloatingPointTypeOperations> (a: T, b: T) -> T {
// ...
var c = T(0) // OK
c += a * b // OK
return c
}
协议的一个好处:它已经被许多整数类型所采用,而对于我们的自定义协议,所有这些int类型(如果我们想要在它们中使用它们)我们的通用)需要明确扩展以采用我们的自定义协议。
要将其包装起来:如果您明确知道要通用的覆盖哪些类型,您可能会膨胀编写自己的自定义协议并扩展这些类型以适应这一点。如果你想要所有(众多)不同的整数类型,对于int和float使用两个单独的泛型,对于后者使用协议protocol MyCustomProtocol {
func *(lhs: Self, rhs: Self) -> Self
func += (inout lhs: Self, rhs: Self)
init(_ value: Int)
// ... other necessary integer and floating point blueprints
}
extension Int: MyCustomProtocol {}
extension Float: MyCustomProtocol {}
extension Double: MyCustomProtocol {}
func myIntAndFloatGenericFunction<T: MyCustomProtocol> (a: T, _ b: T) -> T {
// ...
var c = T(0) // OK
c += a * b // OK
return c
}
let aInt = 2
let bInt = 3
let aInt32: Int32 = 2
let bInt32: Int32 = 3
let aDouble = 2.5
let bDouble = 3.0
let cInt = myIntAndFloatGenericFunction(aInt, bInt) // 6
let cInt32 = myIntAndFloatGenericFunction(aInt32, bInt32) // error
let cDouble = myIntAndFloatGenericFunction(aDouble, bDouble) // 7.5
,可能更喜欢。