2014-11-02 9 views
3

Я строю общий векторный класс в Swift с тремя типами: Float, Double и Int. Это работает до сих пор, но когда я пытаюсь вычислить длину вектора, я сталкиваюсь с проблемой.Общий квадратный корень в Swift

Формула длины вектора представляет собой квадратный корень из (x² + y²). Но так как я использую общий класс для своих векторов, значения x и y называются T.

Функция sqrt Swift принимает только 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' defined? –

+0

Почему бы вам не написать свою собственную функцию sqrt. Мне кажется, что для меня что-то похожее на функциональность c11, то вы можете настроить целые числа на пол, пол и т. Д. или использовать произвольную точность lib –

ответ

1

Я вижу, что вы используете, используя протокол пользовательских Arithmetic для тягот родовых.

Мой подход должен был бы объявить 2 необходимые методы в этом протоколе: toDouble() и fromDouble(), а также осуществлять как в Float, Double и Int расширения. Обратите внимание, что fromDouble() должен быть статическим методом.

Таким образом, вы можете конвертировать T в Double, следовательно, быть в состоянии использовать sqrt(), и конвертировать обратно из Double в T.

Наконец, есть ошибка в вашем коде: если left является пустым вектором, функция будет крах, потому что код в цикле не будет выполнен, так result сохранит nil начального значения. Принудительное развертывание в заявлении return не будет выполнено, что приведет к исключению.

+0

Я теперь применил метод toDouble() в протоколе ArithmeticType и сделал 'sqrt ((self ⋅ self) .toDouble())' в переменной длины.Но теперь Swift выдает эту ошибку: «Не могу вызывать« sqrt »с аргументом типа« Double », тогда как при вводе« sqrt' Xcode автокомпостирует его и показывает, что sqrt (Double) действителен. код внутри моего протокола ArithmeticType теперь: ArithmeticType протокол { функ toDouble() -> Двойной } расширение Int: ArithmeticType { функ toDouble() -> Двойной { Самовозврат } } Реализации Float и Double равны –

+0

Я думаю, что 'func toDouble() -> Double {return self}' должно быть 'func toDouble() -> Double {return Double (self)}' - вам нужно преобразовать, так как нет неявный перевод из 'Int' в' Double' – Antonio

+0

Теперь я применил это изменение, но функция sqrt по-прежнему ожидает двойной аргумент, хотя я su с помощью двойного аргумента. Кажется, он правильно преобразует его, потому что перед .toDouble() он дал мне аргумент типа T. –

0

В Свифте нет общего типа 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

В Swift 3, просто использовать FloatingPoint протокол, который является частью стандартной библиотеки вместо вашего протокола ArithmeticType. Float и Double соответствуют протоколу FloatingPoint. Протокол FlotingPoint имеет метод squareRoot(), так

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

должен сделать трюк.

Не нужно импортировать библиотеки или выполнять проверку типа времени выполнения! Вызов этого метода превращается в встроенный LLVM, поэтому нет никаких служебных вызовов функций. На x86 sqareRoot() должен просто сгенерировать одну инструкцию машинного языка, оставив результат в регистре для оператора возврата для копирования.