原始类型(Int、Float、Double)的泛型会产生奇怪的错误消息

标签 generics swift

这里有人可以看一下代码并告诉我它有什么问题吗?我基本上尝试构建一些通用函数,这些函数对某些原始类型(如 IntFloatDouble 等)进行操作。

不幸的是我无法让它正常工作。这是有效的代码(部分):

// http://stackoverflow.com/a/24047239/2282430
protocol SummableMultipliable: Equatable {
    func +(lhs: Self, rhs: Self) -> Self
    func *(lhs: Self, rhs: Self) -> Self
}

extension Double: SummableMultipliable {}

func vec_dot<T where T: SummableMultipliable>(a : [T], b: [T]) -> Double {
    assert(a.count == b.count, "vectors must be of same length")
    var s : Double = 0.0
    for var i = 0; i < a.count; ++i {
        let x = (a[i] * b[i]) as Double
        s = s + x
    }
    return s
}

现在当我写的时候:

 var doubleVec : [Double] = [1,2,3,4]

 vec_dot(doubleVec, doubleVec)

它返回 30 的正确结果。好的,到目前为止一切顺利。当我尝试传递一组 Int 时,事情变得很奇怪:

 extension Int : SummableMultipliable {}
 var intVec : [Int] = [1,2,3,4]
 vec_dot(intVec, intVec)

砰!抛出异常:

 let x = (a[1] * b[1]) as Double
* thread #1: tid = 0x139dd0, 0x00000001018527ad libswiftCore.dylib`swift_dynamicCast + 1229, queue = 'com.apple.main-thread', stop reason = EXC_BREAKPOINT (code=EXC_I386_BPT, subcode=0x0)
  * frame #0: 0x00000001018527ad libswiftCore.dylib`swift_dynamicCast + 1229
    frame #1: 0x000000010d6c3a09 $__lldb_expr248`__lldb_expr_248.vec_dot <A : __lldb_expr_248.SummableMultipliable>(a=Swift.Array<T> at 0x00007fff5e5a9648, b=Swift.Array<T> at 0x00007fff5e5a9640) -> Swift.Double + 921 at playground248.swift:54
    frame #2: 0x000000010d6c15b0 $__lldb_expr248`top_level_code + 1456 at playground248.swift:64
    frame #3: 0x000000010d6c4561 $__lldb_expr248`main + 49 at <EXPR>:0
    frame #4: 0x000000010165b390 FirstTestPlayground`get_field_types__XCPAppDelegate + 160
    frame #5: 0x000000010165bea1 FirstTestPlayground`reabstraction thunk helper from @callee_owned () -> (@unowned ()) to @callee_owned (@in ()) -> (@out ()) + 17
    frame #6: 0x000000010165ab61 FirstTestPlayground`partial apply forwarder for reabstraction thunk helper from @callee_owned () -> (@unowned ()) to @callee_owned (@in ()) -> (@out ()) + 81
    frame #7: 0x000000010165bed0 FirstTestPlayground`reabstraction thunk helper from @callee_owned (@in ()) -> (@out ()) to @callee_owned () -> (@unowned ()) + 32
    frame #8: 0x000000010165bf07 FirstTestPlayground`reabstraction thunk helper from @callee_owned () -> (@unowned ()) to @callee_unowned @objc_block () -> (@unowned ()) + 39
    frame #9: 0x0000000101fedaac CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12
    frame #10: 0x0000000101fe37f5 CoreFoundation`__CFRunLoopDoBlocks + 341
    frame #11: 0x0000000101fe2fb3 CoreFoundation`__CFRunLoopRun + 851
    frame #12: 0x0000000101fe29f6 CoreFoundation`CFRunLoopRunSpecific + 470
    frame #13: 0x000000010208f2b1 CoreFoundation`CFRunLoopRun + 97
    frame #14: 0x0000000101658be8 FirstTestPlayground`top_level_code + 3784
    frame #15: 0x000000010165b3ba FirstTestPlayground`main + 42
    frame #16: 0x0000000103cd9145 libdyld.dylib`start + 1

我尝试执行不同的转换:

let x = Double(a[i] * b[1])

Error: Could not find an overload for 'init' that accepts the supplied arguments.

let y = a[i] * b[1]
let x = Double(y)

Error: Cannot invoke 'init' with an argument of type 'T'.

接下来,我尝试了:

let y = Double(a[i]) * Double(b[1])
let x = y

Error: Cannot invoke '*' with an argument list of type '(Double, Double').

我尝试了很多更多的东西。一旦我尝试将 Int 作为通用类型传递,就没有任何效果了。

也许我只是在这里遗漏了一些基本知识,或者我太笨了,无法理解泛型编程。在 C++ 中,我会在 2 秒内完成。

最佳答案

当用 Int 数组调用时,a[i] * b[i] 是一个 Int 并且不能被转换 到 Doubleas

要解决该问题,您可以更改 vec_dot 函数以返回 T 对象而不是 Double。 要使初始化 var s : T = 0 起作用,您必须使 SummableMultipliable 派生自 IntegerLiteralConvertible(IntDouble 已经符合):

protocol SummableMultipliable: Equatable, IntegerLiteralConvertible {
    func +(lhs: Self, rhs: Self) -> Self
    func *(lhs: Self, rhs: Self) -> Self
}

func vec_dot<T where T: SummableMultipliable>(a : [T], b: [T]) -> T {
    assert(a.count == b.count, "vectors must be of same length")
    var s : T = 0
    for var i = 0; i < a.count; ++i {
        let x = (a[i] * b[i])
        s = s + x
    }
    return s
}

例子:

var doubleVec : [Double] = [1,2,3,4]
let x = vec_dot(doubleVec, doubleVec)
println(x) // 30.0 (Double)
var intVec : [Int] = [1,2,3,4]
let y = vec_dot(intVec, intVec)
println(y) // 30 (Int)

或者,如果向量积总是产生一个Double,你可以 将 doubleValue() 方法添加到 SummableMultipliable 协议(protocol):

protocol SummableMultipliable: Equatable {
    func +(lhs: Self, rhs: Self) -> Self
    func *(lhs: Self, rhs: Self) -> Self
    func doubleValue() -> Double
}

extension Double: SummableMultipliable {
    func doubleValue() -> Double { return self }
}

extension Int : SummableMultipliable {
    func doubleValue() -> Double { return Double(self) }
}

func vec_dot<T where T: SummableMultipliable>(a : [T], b: [T]) -> Double {
    assert(a.count == b.count, "vectors must be of same length")
    var s : Double = 0
    for var i = 0; i < a.count; ++i {
        let x = (a[i] * b[i]).doubleValue()
        s = s + x
    }
    return s
}

备注:正如@akashivskyy 所说的那样,循环应该写得更快

for i in 0 ..< a.count { ... }

如果你想炫耀你的同事并给他们留下深刻印象或迷惑,那么你可以 用一个表达式替换整个循环:

let s : T = reduce(Zip2(a, b), 0) { $0 + $1.0 * $1.1 }

关于原始类型(Int、Float、Double)的泛型会产生奇怪的错误消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25592838/

相关文章:

ios - 使用滑动手势更改 View Controller

java - 声明扩展某种类型的字段

c# - 奇怪的 C# 泛型约束

ios - UIView.endEditing中的 "force"参数有什么作用?

objective-c - 快速将 UIColor 转换为 CGColor

ios - 如何将按钮设置为可用视口(viewport)高度的一半?

c++ - C++模板特化的思考

c# - 通用接口(interface)的运行时转换

c# - IQueryable<T> 上的通用扩展方法

ios - Swift - 如何以编程方式在它们之间创建带有两个文本和图像的按钮?