2017-02-13 3 views
0

Скажем, у меня есть два структур, которые соответствуют протоколу,Swift операторы протоколов

protocol NumberType { 
    var doubleValue: Double { get } 
} 

struct Rational: NumberType { 
    let numerator: Int 
    let denominator: Int 
    var doubleValue: Double { get { return Double(numerator)/Double(denominator) } } 
} 

struct FixedPoint: NumberType { 
    let value: Double 
    var doubleValue: Double { get { return value } } 
} 

И я хочу, чтобы определить арифметические операторы для NumberType, что в свою очередь, возвращение NumberType с.

func *(lhs: NumberType, rhs: NumberType) -> NumberType { return FixedPoint(lhs.doubleValue * rhs.doubleValue) } 

Но я также хочу, чтобы добавить более конкретные операторы так, когда оба Rational s, я возвращаю Rational.

func *(lhs: Rational, rhs: Rational) -> NumberType { return Rational(...) } 

Есть ли более элегантный способ сделать это, чем иметь одну суперфункцию для каждого оператора, который выполняет все проверки типов?

редактирует

Вы должны быть в состоянии умножить FixedPoint рациональными, так как оба они соответствуют NumberType.

Стратегия перегрузки не будет работать, потому что если я a * b * c, где все значения являются Rational, результат a * b является NumberType, и поэтому (a * b) * c вернется к использованию универсального оператора NumberType умножения. Я потеряю свою точность здесь.

Я не беспокоили о возможности умножать на Doubles, Ints и т.д.

Дальнейшие редактирует

Это не правильно считать, что наиболее общий случай всегда возвращает FixedPoint. Я показываю основное использование здесь, но если у вас есть FixedPoint, равный 0, а еще один Rational, и вы хотели добавить их, вполне возможно, что вы можете получить Rational в своем общем случае.

+0

Фиксированная точка должна быть способна умножаться на рациональную, используя самую общую форму, NumberType * NumberType. –

+0

Извините, исправить пример. Самая общая версия возвращает FixedPoint. –

+0

Надеюсь, вы поймете, что ваша реализация FixedPoint' использует тип с плавающей точкой для хранения значения;) – Hamish

ответ

2

Вы могли бы использовать дженерики здесь ...

Если определить протокол, как ...

protocol NumberType {  
    var doubleValue: Double { get } 

    init(doubleValue double: Double) 
} 

Тогда вы могли бы определить функцию, как ...

func *<T: NumberType>(lhs: T, rhs: T) -> T { 
    return T(doubleValue: lhs.doubleValue * rhs.doubleValue) 
} 

Это позволит вам использовать эту функцию с любым типом объекта, если он соответствует NumberType. Единственное предостережение заключается в том, что оба lhs и rhs должны быть одного типа.

Тогда в реализации каждого NumberType вы можете определить, как работает INIT ...

struct FixedPoint: NumberType { 
    let value: Double 
    var doubleValue: Double { get { return value } } 

    init(doubleValue double: Double) { 
     // this implementation may not be practical for things like Rationals etc... 
     self.value = double 
    } 
} 

Не имея некоторый общий способ создания NumberTypes, то вы должны были бы иметь несколько реализаций, как функция работы и поэтому у вас есть способ создания нескольких экземпляров в зависимости от того, что такое входы.

+0

Я уточнил формулировку в сообщении. Я хочу, чтобы возвращался NumberType. –

+0

В этом случае просто используйте 'T' в качестве возврата ... – Fogmeister

+0

@JacobParker слегка отредактировал, чтобы показать, как вы могли его реализовать, но это может быть непрактично. – Fogmeister

0

Я бы попросил протокол объявить функцию, которая делает то, что вы хотите.

protocol NumberType { 
    var doubleValue: Double { get } 
    func multipliedBy(other: NumberType) -> NumberType 
} 

А затем определим оператор

func *(l: NumberType, r: NumberType) -> NumberType 
{ 
    return l.multipliedBy(other: r) 
} 

Каждый тип, который реализует протокол, реализует multipliedBy(other:) таким образом, что подходит для его типа.

+0

Проблема с этим (и почему вы будете использовать generics) заключается в том, что вы можете использовать Rational и FixedPoint в качестве входных данных ... и что бы вы хотели в качестве вывода? – Fogmeister

+0

@Frogmeister Вам понадобится 'NumberType' в качестве вывода. Вы позволили бы «Rational» решить, что делать, если он умножается на тип, который не является «Rational» – JeremyP

+1

Да, но «NumberType» не является типом. Если бы вы захотели принять два отдельных типа номеров, я бы все же создал подпись, например ... 'func * (lhs: T, rhs: U) -> T' для пример. т. е. всегда возвращать объект типа 'lhs'. Или удалите 'U' целиком и разрешите только однородное использование оператора (который является стандартом Swift для операторов) и перед его использованием преобразовать пользователя (dev) в однородные типы. – Fogmeister

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