2014-11-02 2 views
3

저는 Floft, Double 및 Int의 세 가지 유형으로 Swift에서 일반 벡터 클래스를 작성합니다. 지금까지 작동하지만 벡터의 길이를 계산하려고하면 문제가 발생합니다.Swift의 일반 제곱근

벡터 길이의 공식은 (x² + y²)의 제곱근입니다. 그러나 내 벡터에 제네릭 클래스를 사용하기 때문에 x와 y의 값을 T라고합니다.

Swift의 sqrt 함수는 Double을 인수로 받아 들일 수 있지만 일반 인수는 허용하지 않습니다.

제네릭 매개 변수와 함께 sqrt 함수를 사용할 수있는 방법이 있습니까?

protocol ArithmeticType { 
    func + (left: Self, right: Self) -> Self 
    func - (left: Self, right: Self) -> Self 
    func * (left: Self, right: Self) -> Self 
    func/(left: Self, right: Self) -> Self 
    prefix func - (left: Self) -> Self 

    func toDouble() -> Double 
} 

extension Double: ArithmeticType { 
    func toDouble() -> Double { 
     return Double(self) 
    } 
} 

extension Float: ArithmeticType { 
    func toDouble() -> Double { 
     return Double(self) 
    } 
} 

extension Int: ArithmeticType { 
    func toDouble() -> Double { 
     return Double(self) 
    } 
} 

class Vector<T where T: ArithmeticType, T: Comparable> { 
    var length: T { return sqrt((self ⋅ self).toDouble()) } 
} 

infix operator ⋅ { associativity left } 
func ⋅<T: ArithmeticType> (left: Vector<T>, right: Vector<T>) -> T { 
    var result: T? = nil 

    for (index, value) in enumerate(left.values) { 
     let additive = value * right.values[index] 

     if result == nil { 
      result = additive 
     } else if let oldResult = result { 
      result = oldResult + additive 
     } 
    } 

    if let unwrappedResult = result { 
     return unwrappedResult 
    } 
} 
+1

길이가 부동 소수점 수 (예 : length ([1, 1] = sqrt (2))이기 때문에 일반적으로 Double으로 변환 할 수 있습니다. - Btw, ArithmeticType'이 정의 되었습니까? –

+0

당신은 sqrt 함수를 작성하지 않으시겠습니까? 이것은 c11 기능과 같은 것을 얻으려고 부풀어 오른 많은 코드처럼 보입니다. 그런 다음 정수를 사용자 정의하여 ceil, floor 등을 수행 할 수 있습니다. 또는 임의의 정밀도 라이브러리 사용 –

답변

1

난 당신이 일반을 제약 조건에 사용자 정의 Arithmetic 프로토콜을 사용하여 사용하고있는 것을 볼 : 여기

내가 벡터 길이에 사용하는 코드와 내적의 조각입니다.

내 접근 방식은 프로토콜이 개 필요한 메소드를 선언하는 것입니다 : toDouble()fromDouble()과의 Float, DoubleInt 확장 기능을 모두 구현합니다. fromDouble()은 정적 방법이어야합니다.

당신이 sqrt()을 사용할 수 따라서, TDouble에 변환 한 Double에서 T로 다시 변환 할 수있는이 방법.

마지막으로 코드에 버그가 있습니다. left이 빈 벡터 인 경우 루프의 코드가 절대 실행되지 않으므로 함수가 충돌하므로 resultnil의 초기 값을 유지합니다. return 문에서 강제 unwrapping이 실패하여 예외가 발생합니다.

+0

이제 ArithmeticType 프로토콜에서 toDouble() 메서드를 구현하고 length 변수에서 sqrt ((self/self) .toDouble())를 수행했습니다.그러나 Swift는 다음과 같은 오류를 던집니다 :''Double '형의 인수로'sqrt '를 호출 할 수 없습니다.'sqrt '를 입력하면 Xcode가 그것을 자동 완성하고 sqrt (Double)가 유효 함을 보여줍니다. 내 ArithmeticType 프로토콜 내부 코드는 이제 : 프로토콜 ArithmeticType { FUNC의 toDouble() -> 더블 } 확장 지능 : ArithmeticType { FUNC의 toDouble() -> 더블 { 반환 자기 } } Float과 Double 구현은 같음 –

+0

'func toDouble() -> Double {return self}'는 {func toDouble() -> Double (return Double (self)}'이어야합니다 - 변환해야합니다. Int에서 Double으로의 암시 적 캐스트 – Antonio

+0

이제 그 변경을 적용했지만 sqrt 함수는 여전히 su입니다. 그것을 Double 인수로 전달하십시오. .toDouble() 전에 나에게 T 형의 인수를 주었기 때문에 올바르게 변환하는 것 같습니다. –

0

Swift에는 일반 sqrt가 없습니다. 그러나 당신은 당신 자신의 일반적인 것을 만들 수 있습니다.

import Foundation // for sqrt sqrtf 
public func sqrt<T:FloatingPointType>(v:T) -> T { 
    if let vv = v as? Double { 
     return sqrt(vv) as! T 
    } 
    if let vv = v as? Float { 
     return sqrtf(vv) as! T 
    } 
    preconditionFailure() 
} 
print(sqrt(Float(9))) // == 3 
print(sqrt(Double(9))) // == 3 
+0

Int 케이스를 처리하지 않습니다. –

1

는 스위프트 3에서, 단지 표준 라이브러리 대신 당신의 ArithmeticType 프로토콜의 일부입니다 FloatingPoint 프로토콜을 사용합니다. FloatDoubleFloatingPoint 프로토콜을 준수합니다. FlotingPoint 프로토콜은 squareRoot() 방법이 있으므로

class Vector<T where T: FloatingPoint> { 
    var length: T { return (self ⋅ self).squareRoot() } 
} 

트릭을 할해야합니다.

라이브러리를 가져 오거나 런타임 형식 검사를 수행 할 필요가 없습니다! 이 메서드를 호출하면 LLVM이 내장되어 있으므로 오버 헤드를 호출하는 함수조차 없습니다. x86에서 sqareRoot()는 하나의 기계어 명령어 만 생성하고 결과를 복사 할 return 문에 대한 레지스터로 남겨 두어야합니다.