原始类型(Int,Float,Double)的泛型会产生奇怪的错误消息 [英] Generics of raw types (Int, Float, Double) create weird error messages
问题描述
这里有人可以看一下代码,然后告诉我这是怎么回事吗?我实质上是尝试构建一些对某些原始类型(例如Int
,Float
,Double
等)运行的通用函数.
Can someone here maybe take a look at the code and tell me what's wrong with it? I essentially try to build a couple of generic functions that operate on certain raw types like Int
, Float
, Double
etc.
很遗憾,我无法使其正常运行.这是部分有效的代码:
Unfortunately I can't get it working properly. This is the code that works (partially):
// 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
s数组时,事情变得很奇怪:
It returns the correct result of 30
. Ok, so far so good. Things get weird when I try to pass an array of Int
s:
extension Int : SummableMultipliable {}
var intVec : [Int] = [1,2,3,4]
vec_dot(intVec, intVec)
Bam!引发异常:
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])
错误::找不到接受提供的参数的'init'的重载.
Error: Could not find an overload for 'init' that accepts the supplied arguments.
let y = a[i] * b[1]
let x = Double(y)
错误:无法使用类型为'T'的参数调用'init'.
Error: Cannot invoke 'init' with an argument of type 'T'.
接下来,我尝试了:
let y = Double(a[i]) * Double(b[1])
let x = y
错误:无法使用类型为((Double,Double))的参数列表调用'*'.
Error: Cannot invoke '*' with an argument list of type '(Double, Double').
我尝试了许多更多的东西.一旦我尝试将Int
作为通用类型传递,就不再起作用了.
I tried out many more things. As soon as I try to pass Int
as a generic type, nothing works anymore.
也许我只是在这里遗漏了一些基本知识,或者我太愚蠢而无法理解通用编程.在C ++中,我将在2秒内完成操作.
Maybe I'm just missing something fundamental here or I am just too dumb to understand generic programming. In C++, I'd be done in 2 seconds.
推荐答案
当使用Int
数组调用时,a[i] * b[i]
是Int
,并且不能进行强制转换
到Double
与as
.
When called with an Int
array, a[i] * b[i]
is an Int
and cannot be cast
to Double
with as
.
要解决该问题,可以更改vec_dot
函数以返回T
对象而不是Double
.
要使初始化var s : T = 0
工作,必须使SummableMultipliable
从IntegerLiteralConvertible
派生(Int
和Double
已经符合):
To solve that problem, you can change your vec_dot
function to return a T
object instead of a Double
.
To make the initialization var s : T = 0
work, you have to make SummableMultipliable
derive from IntegerLiteralConvertible
(to which Int
and Double
already conform):
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
,则可以
在SummableMultipliable
协议中添加doubleValue()
方法:
Alternatively, if the vector product should always produce a Double
, you can
add a doubleValue()
method to the 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正确说的那样,循环应更迅速地编写为
Remark: As @akashivskyy correctly said, the loop should be written more swiftly as
for i in 0 ..< a.count { ... }
如果您想看中和打动或迷惑您的同事,则可以 用单个表达式替换整个循环:
If you want to get fancy and impress or puzzle your co-workers then you can replace the entire loop with a single expression:
let s : T = reduce(Zip2(a, b), 0) { $0 + $1.0 * $1.1 }
这篇关于原始类型(Int,Float,Double)的泛型会产生奇怪的错误消息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!