2015-07-29 2 views
2

Я пытаюсь дать любому числу возможность поднятия до произвольной мощности (в частности, 2). Это похоже на прекрасную возможность использовать расширение протокола вместо добавления расширения к Float, Double, Int и т.д.Расширение протокола Swift 2 с использованием `pow`

protocol Raisable { 
    func raise(exponent : Self) -> Self 
} 

extension Raisable where Self : SignedNumberType { 
    func raise(exponent : Double) -> Self { 
     return Self(pow(Double(self), exponent)) 
    } 
} 

protocol Squarable : Raisable { 
    func squared() -> Self 
} 

extension Squarable { 
    func squared() -> Self { 
     return self.raise(2) 
    } 
} 

Составителя шоу «Не удается найти инициализатор для типа„Double“, который принимает список аргументов типа" (Self)».

Любые идеи, как я могу изменить raise:, чтобы обойти это?

Заранее спасибо.

ответ

4

Здесь есть много перекрывающихся проблем, которые могут не удаться хорошо (и результат будет не очень хорошим, даже если вы это сделали). Пойдем через некоторые из них.

protocol Raisable { 
    func raise(exponent : Self) -> Self 
} 

ОК, у нас уже есть наша первая проблема. Рассмотрим, если Self - Int. Каков результат Int(2).raise(-1)? Это должно быть 0,5, но это не целое число. Вы планируете округлить до 1? Это определенно отличается от кода, который вы напишете для Double.

extension Raisable where Self : SignedNumberType { 
    func raise(exponent : Double) -> Self { 
     return Self(pow(Double(self), exponent)) 
    } 
} 

Это требует, чтобы все возможные SignedNumberType быть конвертированы в и из Double, который не обещал, или даже желательным. Например, комплексное число соответствует всем требованиям SignedNumberType, и разумно поднять комплексное число до реального показателя, но тогда pow не является правильной функцией. Вам действительно нужно будет обрабатывать эти случаи с помощью дифференцированного кода. Рассмотрим, в частности, случай i^2, который является реальным, поэтому просто проецируем i на его реальный компонент (0), а затем на квадрат, что приведет к очень неожиданному результату.

extension Squarable { 
    func squared() -> Self { 
     return self.raise(2) 
    } 
} 

Помимо других проблем, это очень, очень медленно (порядков медленнее, чем self*self или self<<1 где применимо). Это не конец света, если он значительно улучшает читаемость, но на самом деле это не похоже.

Вообще говоря, Swift не поощряет функции, которые берут «некоторое число, меня не волнует, какой тип». В большинстве случаев вам нужно написать код для обработки числового преобразования и рассмотреть случаи переполнения, усечения и т. Д. Если вы хотите рекламировать все до Double, вам обычно нужно делать это намеренно, а не через протокол.

Но о расширениях еще есть, что можно узнать. Мы можем, конечно, легко создать метод squared() и прикрепить его к различным типам. Например:

protocol Multipliable { 
    func *(lhs: Self, rhs: Self) -> Self 
} 

extension Multipliable { 
    func squared() -> Self { 
     return self * self 
    } 
} 

extension Int: Multipliable {} 
extension Double: Multipliable {} 

2.squared() 
(2.1).squared() 
+0

Привет, Роб, спасибо, что нашли время, чтобы написать такой исчерпывающий ответ. Похоже, мой энтузиазм по поводу использования новых инструментов на этот раз превзошел мое понимание их. Ура! – Alex

+0

Нет проблем. Я написал, как будет выглядеть ваша функция squared(). Сделано так, нет проблем с этим. Вам просто нужно быть более ясным и убедиться, что вы только полагаетесь на свои функции на то, что вам нужно в ваших протоколах. –

Смежные вопросы