xBA*_*ACP 5 generics macos foundation ios swift
我使用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
}
Run Code Online (Sandbox Code Playgroud)
对于FloatingPointType版本,编译器说"二进制运算符'*'不能应用于两个'T'操作数".但是,它不会在IntegerType版本上执行此操作.为什么?
该FloatingPointType协议确实采用 Double和Float类型,但相反,协议由于某种原因,不包括运算符方法的蓝图,如(在您的情况下),*二元运算符或+=赋值运算符.请注意,仅仅因为某些已知类型采用协议的重要性,该协议本身不一定包含我们期望采用它的那些类型的所有蓝图.
IntegerType另一方面,该协议包括运营商方法的蓝图.
因此,T符合协议的通用FloatingPointType不一定(在swift的眼中)可以乘法等等,因为协议不包含此类操作的蓝图.如果我们看一下对FloatingPointType标准库的参考,我们看到它是貌似只有通过的 Double,Float(和CGFloat).我们知道所有这三种类型都可以通过我们的常规操作员自行运行,所以嘿,为什么我们不能在通用符合上使用那些运算符FloatingPointType?同样,符合协议的类型集合实际上并没有深入了解协议包含的蓝图.
例如,请查看以下协议中某些基本类型的扩展以符合它
protocol ReallyLotsOfAdditionalStuff {}
extension Int : ReallyLotsOfAdditionalStuff {}
extension Double : ReallyLotsOfAdditionalStuff {}
Run Code Online (Sandbox Code Playgroud)
此伪协议的库引用将列出仅仅类型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!
}
Run Code Online (Sandbox Code Playgroud)
因此,要修复您的问题FloatingPointType,请T在函数头中添加自定义协议作为附加类型约束:
func linconv<T: protocol<FloatingPointType, MyNecessaryFloatingPointTypeOperations>>(signal_A: [T], signal_B: [T]) -> [T]? {
// ...
}
Run Code Online (Sandbox Code Playgroud)
或者,让您自己的协议继承FloatingPointType并添加"子协议"所需的任何其他方法,例如:
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]? {
// ...
}
Run Code Online (Sandbox Code Playgroud)
最后,我们可能会问,我们是否FloatingPointType首先需要协议(即使是作为我们自定义协议的父协议)?如果我们只想做一个普通的用于处理两个快捷浮点类型Double和Float,那么我们不妨仅适用于协议MyNecessaryFloatingPointTypeOperations作为一种约束我们的通用:
func myFloatingPointGenericFunction<T: MyNecessaryFloatingPointTypeOperations> (a: T, b: T) -> T {
// ...
return a * b
}
Run Code Online (Sandbox Code Playgroud)
您可能已经知道,我们需要通用符合FloatingPointType单个蓝图的协议:确定我们的泛型函数,我们可以T使用整数初始化器初始化实例,即init(_ value: Int).例如,在你的功能中:
// new array for result
var resultSignal = [T](count: N, repeatedValue: T(0)) // <--
Run Code Online (Sandbox Code Playgroud)
但是,如果这是我们从FloatingPointType协议中使用的唯一蓝图,我们不妨将其作为蓝图添加到我们自己的协议中,并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
}
Run Code Online (Sandbox Code Playgroud)
有了这个,我们意识到我们并不需要为整数类型和浮点类型分别使用两个单独的泛型.因为我们(对于你的例子)只需要1. by-int-initializer,2.*binary operator和3. + =赋值运算符,我们可以为符合这些蓝色标记的所有类型构造泛型作为类型约束,并通过该协议扩展我们希望通用的类型.
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
Run Code Online (Sandbox Code Playgroud)
然而,在这里,我们看到使用现有IntegerType协议的一个好处:它已被许多整数类型采用,而对于我们的自定义协议,所有这些int类型(如果我们想在通用中使用它们)需要显式扩展采用我们的自定义协议.
要将其包装起来:如果您明确知道要通用的覆盖哪些类型,您可能会膨胀编写自己的自定义协议并扩展这些类型以适应这一点.如果你想要所有(众多)不同的整数类型,使用两个单独的通用的int和浮点数,IntegerType后者的协议,可能更喜欢.
| 归档时间: |
|
| 查看次数: |
219 次 |
| 最近记录: |