ios - Swift 泛型 : IntegerType Version Works But Not FloatingPointType

标签 ios swift macos generics foundation

我使用 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 版本上执行此操作。为什么?

最佳答案

FloatingPointType Double确实采用了协议(protocol)和 Float类型,但相反,由于某种原因,该协议(protocol)不包括运算符方法的蓝图,例如(在您的情况下)*二元运算符或 +=赋值运算符。请注意,仅仅因为某些已知类型采用协议(protocol),该协议(protocol)本身不一定包含我们对采用它的那些类型所期望的所有蓝图的重要性。
IntegerType另一方面,协议(protocol)确实包括操作符方法的蓝图。

因此,您的通用 T符合协议(protocol) FloatingPointType不一定(在 swift 眼中)可乘等,因为该协议(protocol)不包含此类操作的蓝图。如果我们看 standard library reference for FloatingPointType ,我们看到它似乎只有 Double 才采用, Float (和 CGFloat )。我们知道所有这三种类型都可以很好地使用我们的常规运算符,所以嘿,为什么我们不能在符合 FloatingPointType 的泛型上使用这些运算符? ?再次,符合协议(protocol)的类型集合实际上无法洞察该协议(protocol)包含哪些蓝图 .

作为一个例子,看下面的协议(protocol),一些基本类型的扩展以符合它

protocol ReallyLotsOfAdditionalStuff {}
extension Int : ReallyLotsOfAdditionalStuff {}
extension Double : ReallyLotsOfAdditionalStuff {}

此虚拟协议(protocol)的库引用将仅列出类型 IntDouble采用它。相反,如果我们不小心,我们可以期望泛型符合协议(protocol) ReallyLotsOfAdditionalStuff至少可以说,可添加(除了许多额外的东西),但自然地,情况并非如此。

无论如何,您可以自己解决这个问题,但是,通过创建一个新协议(protocol),您可以将其添加为泛型 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 ,添加您的自定义协议(protocol)作为 T 的附加类型约束在函数头中:
func linconv<T: protocol<FloatingPointType, MyNecessaryFloatingPointTypeOperations>>(signal_A: [T], signal_B: [T]) -> [T]? { 
    // ...
}

或者,让您自己的协议(protocol)继承 FloatingPointType并在您的“子协议(protocol)”中添加所需的任何其他方法,例如:
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 吗?首先是协议(protocol)(甚至作为我们自定义协议(protocol)的父协议(protocol))?如果我们只想制作一个泛型来处理两个 swift 浮点类型 DoubleFloat ,那么我们不妨只应用协议(protocol) MyNecessaryFloatingPointTypeOperations作为我们泛型的类型约束:
func myFloatingPointGenericFunction<T: MyNecessaryFloatingPointTypeOperations> (a: T, b: T) -> T {
    // ...
    return a * b
} 

您可能已经知道,我们需要泛型符合 FloatingPointType单个蓝图的协议(protocol):确定我们可以初始化的通用函数 T使用整数初始化器的实例,即 init(_ value: Int) .例如,在您的函数中:
// new array for result
var resultSignal = [T](count: N, repeatedValue: T(0)) // <--

但是,如果这是我们使用的唯一蓝图,来自 FloatingPointType协议(protocol),我们不妨将其作为蓝图添加到我们自己的协议(protocol)中,并将泛型类型约束移除到 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
}

有了这个,我们意识到我们真的不需要整数类型和浮点类型的两个单独的泛型。由于我们(对于您的示例)只需要 1. 一个 by-int-initializer、2. * 二元运算符和 3. += 赋值运算符,我们可以为符合这些蓝色标记的所有类型构造一个泛型作为类型约束,并扩展我们希望由本协议(protocol)的泛型覆盖的类型。
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

然而,在这里,我们看到了使用现有 IntegerType 的好处。协议(protocol):它已经被许多整数类型采用,而对于我们的自定义协议(protocol),所有这些 int 类型(如果我们想在我们的泛型中使用它们)都需要显式扩展以采用我们的自定义协议(protocol)。

总结一下:如果您明确知道您的泛型涵盖哪些类型,您可以编写自己的自定义协议(protocol)并扩展这些类型以适应这一点。如果你想要所有(无数)不同的整数类型,使用两个单独的整数和浮点泛型,协议(protocol) IntegerType对于后者,大概是偏爱。

关于ios - Swift 泛型 : IntegerType Version Works But Not FloatingPointType,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34458276/

相关文章:

iphone - 在phonegap中重定向页面

ios - 使用叠加层后 SwiftUI 边缘可见

cocoa - 如何在界面生成器中创建 cocoa 绑定(bind)?

objective-c - 如何获取 NSDocument 子类来打印自定义 View

linux - 在 macOS Sierra 上的 sh 文件中为单个命令禁用 sudo

ios - CGContext横竖线宽的区别

ios - 将守卫与多种类型一起使用

ios - 日期格式在 swift 中无法正常工作

iOS Apple HealthKit 仅限 Apple Watch 步数数据

ios - StageWebView、iOS、本地文件