2016-09-13 1 views
6

Это очень сложно выразить словами, но я создал минимальный пример.Тип структуры, которая соответствует общему протоколу с ассоциированным типом, который также является протоколом

Вот суть, если вы предпочитаете ... https://gist.github.com/anonymous/67d83fb2f286cf84539b58be96a971d3

Протокол «элемент данных»

У меня есть протокол, который определяет Sortable объекты со свойством number как так.

protocol Sortable: Comparable { 
    var number: Int {get} 

    static func < (lhs:Self, rhs: Self) -> Bool 
    static func == (lhs:Self, rhs: Self) -> Bool 
} 

struct BasicSortable: Sortable { 
    let number: Int 

    static func < (lhs:BasicSortable, rhs: BasicSortable) -> Bool { 
     return lhs.number < rhs.number 
    } 

    static func == (lhs:BasicSortable, rhs: BasicSortable) -> Bool { 
     return lhs.number == rhs.number 
    } 
} 

«Рабочий» протокол

Тогда у меня есть протокол, который может сделать что-то с этим Sortable типов. Но поскольку он имеет требование Само это должно быть определено в качестве протокола с соответствующим типом и в структурах как родовой собственности ...

protocol Sorter { 
    associatedtype Item: Sortable 

    func sort(items: [Item]) -> [Item] 
} 

// Two different sorters 
struct AscendingSorter<T:Sortable>: Sorter { 
    typealias Item = T 

    func sort(items: [T]) -> [T] { 
     return items.sorted() 
    } 
} 

struct DescendingSorter<T:Sortable>: Sorter { 
    typealias Item = T 

    func sort(items: [T]) -> [T] { 
     return items.sorted{$0 > $1} 
    } 
} 

Обработчик

Наконец структуру, которая тянет все вместе ...

struct DataHandler<T: Sortable> { 
    let items: [T] 
    let sortedItems: [T] 

    init(unsortedItems: [T]) { 
     items = unsortedItems 

     let sorter = AscendingSorter<T>() 
     sortedItems = sorter.sort(items: unsortedItems) 
    } 
} 

делая это все работы

Все это работает.

let array = [ 
    BasicSortable(number: 1), 
    BasicSortable(number: 8), 
    BasicSortable(number: 13), 
    BasicSortable(number: 3), 
    BasicSortable(number: 4), 
    BasicSortable(number: 14), 
    BasicSortable(number: 5), 
    BasicSortable(number: 12), 
    BasicSortable(number: 3), 
] 

let handler = DataHandler(unsortedItems: array) 

handler.sortedItems 

Это выводит массив элементов в правильном порядке, в зависимости от того, какого типа сортировщика я создаю в обработчике

Проблемы

То, что я пытаюсь сделать сейчас чтобы найти объявление свойства для этой структуры sorter, которая может принимать в нее ЛЮБОЙ тип Сортировщика, но все, что я пробовал делать, до сих пор не удалось.

Есть ли способ сделать это?

В структуры я хотел бы иметь ...

let sorter: SomeTypeHere 

А потом в INIT установить его как ...

sorter = AscendingSorter() 

, но ни одна комбинация я не пытался в этом работал.

Благодаря

+0

Есть ли 'Sorte Протокол r' должен иметь «ассоциированный тип»? Почему бы не сделать метод sort (items:) 'generic (например, func sort (items: [T]) -> [T]')? Другими словами, должен ли каждый экземпляр «AscendingSorter» или «DescendingSorter» иметь один конкретный тип «Sortable», который он может сортировать? Хотя, как говорится, метод 'sort (items:)' даже должен быть методом экземпляра - я не понимаю, почему вам нужно будет использовать любое состояние экземпляра. – Hamish

+0

@ Хамиш, я вижу. Подожди. Я отдам это. – Fogmeister

+0

BTW, Swift 2.3 не имеет проблем с кодом, который вы указали выше. – NRitH

ответ

3

Вы можете использовать тип стирания, чтобы реализовать свой собственный AnySorter.

Начиная с собственным кодом свыше:

protocol Sortable: Comparable { 
    var number: Int {get} 

    /* as Hamish mentions in his answer: 
     < and == already blueprinted in Comparable and Equatable */ 
} 

protocol Sorter { 
    associatedtype Item: Sortable 

    func sort(items: [Item]) -> [Item] 
} 

Построить AnySorter:

struct AnySorter<Item: Sortable>: Sorter { 
    private let _sort: ([Item]) -> [Item] 

    init<S: Sorter where S.Item == Item>(_ sorter: S) { 
     _sort = sorter.sort 
    } 

    func sort(items: [Item]) -> [Item] { 
     return _sort(items) 
    } 
} 

Что вы используете, например, в качестве аргумента инициализаторе в вашем DataHandler:

struct DataHandler<T: Sortable> { 
    let items: [T] 
    let sortedItems: [T] 

    init(unsortedItems: [T], sorter: AnySorter<T>) { 
     items = unsortedItems 
     sortedItems = sorter.sort(items: unsortedItems) 
    } 
} 

Ваш обработчик теперь может быть использован с типом стертой AnySorter применяется к вашим Sortable типов. Например, для двух простых сортировочных вы поставляется в вашем вопросе:

struct AscendingSorter<T:Sortable>: Sorter { 
    typealias Item = T 

    func sort(items: [T]) -> [T] { 
     return items.sorted() 
    } 
} 

struct DescendingSorter<T:Sortable>: Sorter { 
    typealias Item = T 

    func sort(items: [T]) -> [T] { 
     return items.sorted{$0 > $1} 
    } 
} 

/* example usage */ 
extension Int: Sortable { 
    var number: Int { return self } 
} 

let arr = [1, 4, 2, 8, 3] 

let dataHandlerDesc = DataHandler(unsortedItems: arr, sorter: AnySorter(DescendingSorter())) 
print(dataHandlerDesc.sortedItems) // [8, 4, 3, 2, 1] 

let dataHandlerAsc = DataHandler(unsortedItems: arr, sorter: AnySorter(AscendingSorter())) 
print(dataHandlerAsc.sortedItems) // [1, 2, 3, 4, 8] 

Редактировать дополнение ответить на ваш комментарий:

Можно ли принимать входной параметр и сохранить его в недвижимость? Я бы использовал AnySorter<T> как тип недвижимости?

Да, вы можете держать недвижимость в DataHandler с типом AnySorter. Например, для надуманного примера мы можем позволить sortedItems быть вычисленным свойством, которое использует экземпляр AnySorter для сортировки сохраненного списка элементов (конечно, в действительности мы не хотим делать эту повторную сортировку для каждого вызова, но только для примера):

struct DataHandler<T: Sortable> { 
    let items: [T] 
    var sortedItems: [T] { return sorter.sort(items: items) } 
    var sorter: AnySorter<T> 

    init(unsortedItems: [T], sorter: AnySorter<T>) { 
     items = unsortedItems 
     self.sorter = sorter 
    } 

    mutating func changeSorter(newSorter: AnySorter<T>) { 
     sorter = newSorter 
    } 
} 

/* example usage */ 
extension Int: Sortable { 
    var number: Int { return self } 
} 

let arr = [1, 4, 2, 8, 3] 

var dataHandler = DataHandler(unsortedItems: arr, sorter: AnySorter(DescendingSorter())) 
print(dataHandler.sortedItems) // [8, 4, 3, 2, 1] 

dataHandler.changeSorter(newSorter: AnySorter(AscendingSorter())) 
print(dataHandler.sortedItems) // [1, 2, 3, 4, 8] 
+0

ОК, мне это нравится: D Можно ли взять входной параметр и сохранить его в свойстве? Я бы просто использовал 'AnySorter ' как тип свойства? – Fogmeister

+1

@Fogmeister уверен, я добавил пример такого случая. – dfri

+0

Отлично, спасибо: D Это позволит мне делать то, что я хотел, включая значение по умолчанию для сортировщика и другие подобные вещи: D Спасибо – Fogmeister

0

Ваш sorter свойство не может быть объявлен как обычный Sorter, потому что, как вы отметили, это имеет Self требование, но я верю, что вы можете это сделать, если добавить второй аргумент типа к вашему DataHandler, так что он выглядит как

struct DataHandler<T: Sortable, S: Sorter> { 
    let items: [T] 
    let sortedItems: [T] 
    let sorter: S 

    init(unsortedItems: [T], sorter: S) { 
     items = unsortedItems 

     self.sorter = sorter 
     sortedItems = self.sorter.sort(items: unsortedItems) 
    } 
} 
+0

Проблема, которую я имею, заключается в том, что Sorter S больше не знает, с какими типами элементов он работает. Поэтому он не будет принимать 'unsortedItems' как параметр в функции сортировки. – Fogmeister

3

Если экземпляр данного типа, который соответствует Sorter может справиться с любой однородной массив элементов, которые соответствуют Sortable (если оно ограничивается одним конкретным типом, то @dfri's answer имеет получил вас накрыл) - тогда Sorter не обязательно иметь associatedtype. Вместо этого вы можете просто создать общий метод sort(items:), который позволит вам использовать Sorter как тип.

Также, если ваш метод sort(items:) не использует какое-либо состояние экземпляра (это не в вашем примере кода), вы можете сделать его static - и просто обходите типы сортировщиков, а не экземпляры.

Например, ваш протокол Sortable и BasicSortable реализации:

protocol Sortable : Comparable { 
    var number : Int { get } 

    // note that you don't need to re-define the < and == operator requirements, 
    // as they're already defined by Comparable and Equatable 
} 

struct BasicSortable : Sortable { 

    let number : Int 

    static func < (lhs:BasicSortable, rhs: BasicSortable) -> Bool { 
     return lhs.number < rhs.number 
    } 

    static func == (lhs:BasicSortable, rhs: BasicSortable) -> Bool { 
     return lhs.number == rhs.number 
    } 
} 

Ваш протокол Sorter и различные реализации сортировщик:

protocol Sorter { 

    // A sort function that can take any homogenous array of a given 
    // Sortable element (meaning that an instance of a type that conforms to 
    // Sorter isn't restricted to a single concrete type of Sortable). 
    // As the function doesn't rely on any instance state, it's static. 
    static func sort<T:Sortable>(items: [T]) -> [T] 
} 

// Two different sorters 
enum AscendingSorter : Sorter { 
    static func sort<T:Sortable>(items: [T]) -> [T] { 
     return items.sorted(by: <) 
    } 
} 

enum DescendingSorter : Sorter { 
    static func sort<T:Sortable>(items: [T]) -> [T] { 
     return items.sorted(by: >) 
    } 
} 

И, наконец, ваш DataHandler с примером использования:

struct DataHandler<T: Sortable> { 

    let items: [T] 
    private(set) var sortedItems: [T] 

    var sorter : Sorter.Type { // simply hold a given type of sorter 
     willSet { 
      if sorter != newValue { 
       // re-sort items upon (different) sorter being set 
       sortedItems = newValue.sort(items: items) 
      } 
     } 
    } 

    init(unsortedItems: [T], sorter: Sorter.Type) { 
     items = unsortedItems 
     self.sorter = sorter 
     sortedItems = sorter.sort(items: unsortedItems) 
    } 
} 

let items = [BasicSortable(number: 2), BasicSortable(number: 4), BasicSortable(number: 6), 
      BasicSortable(number: 1), BasicSortable(number: 4)] 

var handler = DataHandler(unsortedItems: items, sorter: AscendingSorter.self) 
print(handler.sortedItems) 

// [BasicSortable(number: 1), BasicSortable(number: 2), BasicSortable(number: 4), 
// BasicSortable(number: 4), BasicSortable(number: 6)] 

handler.sorter = DescendingSorter.self 
print(handler.sortedItems) 

// [BasicSortable(number: 6), BasicSortable(number: 4), BasicSortable(number: 4), 
// BasicSortable(number: 2), BasicSortable(number: 1)] 
+0

Большое спасибо. Мне нравится идея использования статического метода и передачи типа, а не экземпляра. Я удалил объявление <и == Я не думал прямо, когда я добавил их. Конечно, Comparable добавляет их: D Мне также нравится использование триггера 'willSet' на сортировщике. Спасибо: D Я чуть не отказался от двух отличных ответов менее чем за час: D #winning! – Fogmeister

+0

@Fogmeister Рад помочь :) – Hamish

+2

Уход! Теперь действительно очень тщательный комбинированный ответ на вопрос OPs :) – dfri

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