2015-12-26 5 views
1

Я хочу расширить типизированный массив Array<SomeType> так, чтобы он соответствовал протоколу SomeProtocol. Теперь я знаю, что вы можете расширить типизированный массив, как показано ниже:Расширение типизированного массива в соответствии с протоколом в Swift 2

extension Array where Element: SomeType { ... } 

И вы также можете расширить объект в соответствии с протоколом, например, так:

extension Array: SomeProtocol { ... } 

Но я не могу понять, что это право синтаксиса иметь массив типизированных соответствовать протоколу, что-то вроде:

extension (Array where Element: SomeType): SomeProtocol { ... } 

Все Swift 2 специалисты знают, как это сделать?

+0

Посмотрите на протокол StringType в этом ответе, чтобы расширить массив строк http://stackoverflow.com/a/33862452/2303865 –

+0

Расширение типа ' Массив с ограничениями не может иметь оговорку о наследовании. логики нет. можете ли вы предоставить нам полезный пример? – user3441734

+0

@JamesHu У вас было время посмотреть и дать ответ нашим ответам ниже? – dfri

ответ

3

Вы не можете применять много логики к соответствию. Это либо соответствует, либо не соответствует. Однако вы можете применить немного логики к расширениям. Приведенный ниже код упрощает установку конкретных реализаций соответствия. Это важная часть.

Это используется как типизированное ограничение позже.

class SomeType { } 

Это ваш протокол

protocol SomeProtocol { 

    func foo() 

} 

Это расширение протокола. Реализация foo() в расширении SomeProtocol создает значение по умолчанию.

extension SomeProtocol { 

    func foo() { 
     print("general") 
    } 
} 

Теперь Array соответствует SomeProtocol используя реализацию по умолчанию foo(). Все массивы теперь будут иметь foo() как метод, который не является очень элегантным. Но это ничего не делает, так что никому не больно.

extension Array : SomeProtocol {} 

Теперь интересный материал: Если мы создадим расширение для Array с ограничением по типу для Element мы можем переопределить реализацию по умолчанию foo()

extension Array where Element : SomeType { 
    func foo() { 
     print("specific") 
    } 
} 

Тесты:

let arrayOfInt = [1,2,3] 
arrayOfInt.foo() // prints "general" 

let arrayOfSome = [SomeType()] 
arrayOfSome.foo() // prints "specific" 
1

Я сам обдумывал это, и есть некоторые способы несколько подражать поведению, которое вы сейчас oking для.


Подход № 1

Определение протокола SomeType действовать как тип ограничения для типов, которые вы хотите быть покрыты вашим Array<SomeType> расширением для SomeProtocol; где последний содержит чертежи для некоторых аккуратных методов, которые вы хотите расширить Array.

protocol SomeType { 
    var intValue: Int { get } 
    init(_ value: Int) 
    func *(lhs: Self, rhs: Self) -> Self 
    func +=(inout lhs: Self, rhs: Self) 
} 

extension Int : SomeType { var intValue: Int { return self } } 
extension Double : SomeType { var intValue: Int { return Int(self) } } 
    /* Let's not extend 'Float' for now 
extension Float : MyTypes { var intValue: Int { return Int(self) } } */ 

protocol SomeProtocol { 
    func foo<T: SomeType>(a: [T]) -> Int? 
} 

Теперь вы можете расширить Array к SomeProtocol, и с помощью is ключевого слова, вы можете утверждать, что вы родовое T (сдерживаются SomeType) и элементы Self одного и того же типа, используя is ключевое слово , что, если это правда, следует явной отливка:

extension Array : SomeProtocol { 
    func foo<T: SomeType>(a: [T]) -> Int? { 
     /* [T] is Self? proceed, otherwise return nil */ 
     if let b = self.first { 
      if b is T && self.count == a.count { 
       var myMultSum: T = T(0) 

       for (i, sElem) in self.enumerate() { 
        myMultSum += (sElem as! T) * a[i] 
       } 
       return myMultSum.intValue 
      } 
     } 
     return nil 
    } 
} 

Теперь мы расширенная Array с элементами SomeType с функцией foo(...) blueprin в протоколе SomeProtocol.

/* Tests */ 
let arr1d : [Double] = [1.0, 2.0, 3.0] 
let arr2d : [Double] = [-3.0, -2.0, 1.0] 

let arr1f : [Float] = [1.0, 2.0, 3.0] 
let arr2f : [Float] = [-3.0, -2.0, 1.0] 

func bar<U: SomeType> (arr1: [U], _ arr2: [U]) -> Int? { 
    return arr1.foo(arr2) 
} 
let myInt1d = bar(arr1d, arr2d) // -4, OK 

let myInt1f = bar(arr1f, arr2f) 
    /* Compile time error: "Cannot convert value of type '[Float]' 
     to expected argument type '[_]'"       */ 

Ok! Мы ожидали окончательной ошибки времени компиляции, так как «Float» не соответствует протоколу SomeType.


Подход № 2

Теперь для другого подхода: Я основан на дженерики, которые следуют по this excellent post by Milen Dzhumerov, здесь приспособлены для массива и с некоторыми примерами различных методов расширения.

В этом примере мы реализуем «общее» расширение протокола для Array: S типа Double или Float, представленного протоколом типа ограничения SomeType

protocol SomeType { 
    init(_ value: Int) 
    init(_ value: Double) 
    init(_ value: Float) 
    func == (lhs: Self, rhs: Self) -> Bool 
} 

extension Double: SomeType {} 
extension Float: SomeType {} 

protocol GenericProtocol { 
    typealias AbstractType : SequenceType 
    func repeatNumberNumberManyTimes(arg: Int) -> AbstractType 
    func removeRandomElement(arg: AbstractType) -> AbstractType 
    func countNumberOf42s(arg: AbstractType) -> Int 

} 

Пересылки GenericProtocol к AbstractType (который, здесь, соответствует SequenceType) с использованием структуры, а также осуществлять чертежи протокола в последнем:

struct SomeArrayProtocol<T: SequenceType> : GenericProtocol { 
    private let _repeatNumberNumberManyTimes : (Int) -> T 
    private let _removeRandomElement : (T) -> T 
    private let _countNumberOf42s : (T) -> Int 

    init<P : GenericProtocol where P.AbstractType == T>(_ dep : P) { 
     _repeatNumberNumberManyTimes = dep.repeatNumberNumberManyTimes 
     _removeRandomElement = dep.removeRandomElement 
     _countNumberOf42s = dep.countNumberOf42s 
    } 

    func repeatNumberNumberManyTimes(arg: Int) -> T { 
     return _repeatNumberNumberManyTimes(arg) 
    } 

    func removeRandomElement(arg: T) -> T { 
     return _removeRandomElement(arg) 
    } 

    func countNumberOf42s(arg: T) -> Int { 
     return _countNumberOf42s(arg) 
    } 
} 

Реализовать фактические методы для чертежей GenericProtocol в другой структуре, где теперь общий тип ограничен SomeType тип ограничения (протокол). Следует отметить, что именно эта часть, которая имитирует ваш желает пихты (но прямолинейно недостижимой) формы extension (Array where Element: SomeType): SomeProtocol { ... }:

struct SomeArrayGenericExtensions<T: SomeType> : GenericProtocol { 
    typealias AbstractType = Array<T> 
    func repeatNumberNumberManyTimes(arg: Int) -> [T] { 
     return Array<T>(count: arg, repeatedValue: T(arg)) 
    } 
    func removeRandomElement(arg: [T]) -> [T] { 
     var output = [T]() 
     let randElemRemoved = Int(arc4random_uniform(UInt32(arg.count-1))) 
     for (i,element) in arg.enumerate() { 
      if i != randElemRemoved { 
       output.append(element) 
      } 
     } 
     return output 
    } 
    func countNumberOf42s(arg: [T]) -> Int { 
     var output = 0 
     for element in arg { 
      if element == T(42) { 
       output++ 
      } 
     } 
     return output 
    } 
} 

Наконец, некоторые тесты:

let myGenericExtensionUsedForDouble : SomeArrayProtocol<Array<Double>> = SomeArrayProtocol(SomeArrayGenericExtensions()) 
let myGenericExtensionUsedForFloat : SomeArrayProtocol<Array<Float>> = SomeArrayProtocol(SomeArrayGenericExtensions()) 
// let myGenericExtensionUsedForInt : SomeArrayProtocol<Array<Int>> = SomeArrayProtocol(SomeArrayGenericExtensions()) // Error! Int not SomeType, OK! 

var myDoubleArr = [10.1, 42, 15.8, 42.0, 88.3] 
let my10EntriesOfTenDoubleArr = myGenericExtensionUsedForDouble.repeatNumberNumberManyTimes(10) // ten 10:s 
let myFloatArr : Array<Float> = [1.3, 5, 8.8, 13.0, 28, 42.0, 42.002] 
let myIntArr = [1, 2, 3] 

let a = myGenericExtensionUsedForDouble.countNumberOf42s(myDoubleArr) // 2 
let b = myGenericExtensionUsedForFloat.countNumberOf42s(myFloatArr) // 1 

myDoubleArr = myGenericExtensionUsedForDouble.removeRandomElement(myDoubleArr) // [10.1, 15.8, 42.0, 88.3] 

я несколько неуверенные, имеет ли подход 2 выше на самом деле некоторые практические приложения для массивов или нет (в покрытии Милан он относится к типам без последовательности, возможно, более полезен); это довольно много работы для не столько дополнительного взрыва для доллара. Тем не менее, это может быть поучительным и довольно развлекательным упражнением :)