2014-08-31 2 views
3

여기 누군가가 코드를 살펴보고 잘못된 점을 말해 줄 수 있습니까? 본질적으로 Int, Float, Double원시 형식의 제네릭 (Int, Float, Double) 이상한 오류 메시지가 발생합니다.

과 같은 특정 원시 유형에서 작동하는 몇 가지 일반 함수를 빌드하려고합니다. 불행히도 제대로 작동하지 않습니다.

// 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'.

다음, 나는 시도 :

,

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

나는 시도했다 많은 더 많은 것들. 일반 유형으로 Int을 전달하려고하면 더 이상 작동하지 않습니다.

어쩌면 나는 여기에 근본적인 것을 놓친 것일 수도 있고 아니면 제네릭 프로그래밍을 이해하는 것이 너무 바보 일 수도 있습니다. C++에서는 2 초 만에 끝났습니다. Int 배열로 호출 할 때

답변

5

a[i] * b[i]Int이며 asDouble에 캐스팅 할 수 없습니다.

함수를 변경하면 Double 대신 T 개체를 반환 할 수 있습니다. 초기화 var s : T = 0 작업을하려면, 당신은 SummableMultipliableIntegerLiteralConvertible에서 (이미 일치하는 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() m SummableMultipliable 프로토콜 ethod :

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 } 
+1

약간의 추가로, 나는 c와 유사한 문법 대신에'for i for 0 .. akashivskyy

+0

좋은 답변입니다. 고마워. 나는 Zip2와 함께 마지막 것을 좋아한다. 몰랐는지 스위프트는 기능 프로그래밍에 너무 많은 감명을 받았다. –

3

예외가 발생한 이유는 다운 캐스트라는 것입니다. 공정하게하기 위해, 당신이하려는 것은 불법이며 컴파일러는 처음부터 그렇게 할 수 없습니다. vec_dot 이후

TSummableMultipliable 것을, 그냥 그런 Double로 변환 할 수 없습니다 알고있다 (이이 아니에요 String 알고 생각하는 방법?).

간단한 방법은, 음, 일반 제약 조건을 제거하고 사용지고이 문제를 제거하는 기능을 대신 과부하 :

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

func vec_dot(a: [Int], b: [Int]) -> Double { 
    return vec_dot(a.map({ Double($0) }), b.map({ Double($0) })) 
} 

var doubleVec: [Double] = [1, 2, 3, 4] 
vec_dot(doubleVec, doubleVec) // 30.0 

var intVec: [Int] = [1, 2, 3, 4] 
vec_dot(intVec, intVec) // 30.0 

은 여전히 ​​프로토콜을 고수 할 경우 대신 Martin R's answer을 참조하십시오.

+0

마이너 노트 : 'vec_dot (intVec, intVec)'는 주석이 암시 할 수있는 것처럼 Double이 아니라 (30.0) Int를 반환한다. –

+0

그래, 미안, 그게 전부였다. – akashivskyy

관련 문제