2015-09-25 6 views
0

В пятницу Fun я хотел смоделировать углы в взаимозаменяемых форматах. Я не уверен, что я сделал это самым жестоким идиоматическим способом, но я учусь. Поэтому у меня есть протокол Angle, а затем 3 разных типа структуры (радианы, градусы и вращения), которые все соответствуют протоколу Angle. Я хотел бы иметь возможность добавлять/вычитать их, но трюк заключается в том, что аргумент lhs должен диктовать возвращаемый тип. Например:Быстродействующий двоичный оператор для протоколов, который возвращает тип приемника (который соответствует протоколу)

Degrees(180) + Rotations(0.25) --> Degrees(270) 

или

Rotations(0.25) + Radians(M_PI) -> Rotations(0.75) 

То, что я надеялся, что это я мог сделать что-то вроде

func + (lhs:Angle, rhs:Angle) -> Angle { 
    return lhs.dynamicType(rawRadians: lhs.rawRadians + rhs.rawRadians) 
} 

Протокол Angle требует var rawRadians:CGFloat { get } а также init(rawRadians:CGFloat)

Я мог бы сделать это с помощью Smalltalk-es que двойной диспетчерский подход, но я думаю, что в большинстве случаев более подходящий подход Swift (особенно тот, который требует меньше кода, двойная отправка требует большого количества шаблонов).

ответ

2

Вам просто нужно родовое дополнение:

func +<A: Angle>(lhs: A, rhs: Angle) -> A { 
    return A(rawRadians: lhs.rawRadians + rhs.rawRadians) 
} 

Таким образом, дополнение будет возвращать любой тип на LHS.

Вообще говоря, если вы используете dynamicType, вы, вероятно, сражаетесь с Swift. Swift больше полагается на генерики и протоколы (т. Е. Информацию о статическом типе во время компиляции), а не на динамическую отправку (т. Е. Информацию о динамическом типе во время выполнения).

В этом коде, как вы говорите, A является заполнителем для «определенного типа, который соответствует Angle, который будет определен во время компиляции». Таким образом, в первом примере:

Degrees(180) + Rotations(0.25) --> Degrees(270) 

Это фактически вызывает специальную функцию +<Degrees>. А это:

Rotations(0.25) + Radians(M_PI) -> Rotations(0.75) 

называет (логически) другую функцию под названием +<Rotations>. Компилятор может выбрать оптимизацию этих функций в одну функцию, но логически они являются независимыми функциями, созданными во время компиляции. Это в основном ярлык для записи addDegrees(Degrees, Angle) и addRotations(Rotations, Angle) вручную.

Теперь, на ваш вопрос о функции, которая принимает два угла и возвращает .... ну, что? Если вы хотите вернуть Angle в этом случае, это легко, и точно совпадает с оригинальной подписью:

func +(lhs: Angle, rhs: Angle) -> Angle { 
    return Radians(rawRadians: lhs.rawRadians + rhs.rawRadians) 
} 

«Но ...» вы говорите «который возвращает Radians.» Нет, нет. Он возвращает Angle. Вы можете делать все, что угодно, «под углом». Детали реализации непрозрачны, как и должно быть. Если вам небезразлично, что базовая структура данных - это Radians, вы почти наверняка делаете что-то неправильно.

ОК, есть один боковой корпус, где может быть полезно знать это, и это, если вы печатаете вещи в зависимости от того, как вы их отправляете.Поэтому, если пользователь дал вам информацию о степени, чтобы начать, вы хотите распечатать все в градусах (используя метод description, который вы не указали). И, возможно, что это стоит делать в этом конкретном case.If вы хотите, исходный код был очень близок:

func +(lhs: Angle, rhs: Angle) -> Angle { 
    return lhs.dynamicType.init(rawRadians: lhs.rawRadians + rhs.rawRadians) 
} 

Но очень важно, чтобы понять, что это не соответствует Вашему запросу, чтобы иметь «аргумент LHS диктовать тип возврата. " Это заставляет аргумент lhs диктовать возврат реализации. Возврат тип всегда Angle. Если вы хотите изменить возврат типа, вам необходимо использовать генерики.