2016-09-13 10 views
6

Jest to bardzo trudne do ujęcia w słowa, ale stworzyłem minimalny przykład.Typ struktury zgodnej z protokołem generycznym z powiązanym typem, który jest również protokołem

Oto sedno jeśli wolisz ... https://gist.github.com/anonymous/67d83fb2f286cf84539b58be96a971d3

Protokół „element danych”

Mam protokół, który określa Sortable obiektów z właściwością number jak tak.

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 
    } 
} 

W „pracownik” protokół

Wtedy mam protokół, który może coś zrobić z tymi Sortable typów. Ale ponieważ ma wymogu Samo to musi być zdefiniowany jako protokół z przynależnym typu oraz w elemencie jak własność generycznego ...

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} 
    } 
} 

handler

wreszcie struct, która ściąga wszystko razem ...

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

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

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

Making to wszystko działa

To wszystko działa.

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 

ten wypisuje tablicę elementów w odpowiedniej kolejności w zależności od rodzaju sortowania tworzę w Handler

Problem

Co staram się zrobić to teraz aby znaleźć deklarację właściwości dla tej struktury sorter, która może przyjąć dowolny typ sortera, ale wszystko, co próbowałem w tym przypadku, zawiodło.

Czy istnieje sposób, aby to zrobić?

W struct chciałbym mieć ...

let sorter: SomeTypeHere 

A potem w init ustawić go jak ...

sorter = AscendingSorter() 

ale żadna kombinacja Próbowałem w ten sposób pracował.

Dzięki

+0

Czy "Sorte" protokół r' musi mieć 'typ powiązany '? Dlaczego nie zrobić ogólnej metody 'sort (items:)' (np. Func sort (items: [T]) -> [T] ')? Innymi słowy, czy każda instancja 'AscendingSorter' lub' DescendingSorter' musi mieć pojedynczy konkretny typ "Sortable", który może sortować? Pomimo tego, że jest to powiedziane, czy metoda sort (items:) 'musi być metodą instancji - nie widzę powodu, dla którego musiałbyś użyć dowolnego stanu instancji. – Hamish

+0

@Hamish oh, rozumiem. Wytrzymać. Dam ci to. – Fogmeister

+0

BTW, Swift 2.3 nie ma problemu z kodem, który napisałeś powyżej. – NRitH

Odpowiedz

3

można użyć typu skasowaniu zaimplementować własną AnySorter.

Począwszy własnego kodu z góry:

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] 
} 

skonstruować 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) 
    } 
} 

Które skorzystać z np jako argument do inicjowania w twojej DataHandler:

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

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

Twój przewodnik może być teraz używany z typem usunięte AnySorter stosowanej do typów Sortable. Na przykład dla dwóch prostych sortowniki już dostarczone w swoim pytaniu:

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] 

Edycja dodawanie odpowiedzieć na komentarz:

jest to możliwe do podjęcia parametr wejściowy i przechowywać go w własność? Czy po prostu użyłbym AnySorter<T> jako typu nieruchomości?

Tak, można zachować nieruchomość w DataHandler z typem AnySorter. Np. Dla przykładowego przykładu, możemy pozwolić, aby sortedItems była wyliczoną właściwością, która wykorzystuje instancję AnySorter do sortowania zapisanej listy elementów (oczywiście w rzeczywistości nie chcemy tego ponownego sortowania dla każdego połączenia, ale tylko na tym przykładzie):

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

OK, podoba mi się to: D Czy możliwe jest pobranie parametru wejściowego i zapisanie go w obiekcie? Czy po prostu użyłbym 'AnySorter ' jako typu właściwości? – Fogmeister

+1

@Fogister, dodałem przykład takiego przypadku. – dfri

+0

Doskonale, dziękuję: D Pozwoli mi to zrobić to, co chciałem, w tym wartość domyślną dla sortownika i inne podobne rzeczy: D Dzięki – Fogmeister

0

Twoja nieruchomość sorter nie może być uznana jako zwykły Sorter ponieważ, jak pan zauważył, że ma wymogu Self, ale wierzę, że można to zrobić, jeśli dodać drugi typ argumentu do listy DataHandler, tak, że wygląda jak

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

Mam problem z tym, że Sorter S nie wie już, z jakimi typami elementów pracuje. Więc nie przyjmie "unsortedItems" jako parametru w funkcji sortowania. – Fogmeister

3

Jeśli instancja danego typu, który jest zgodny z Sorter poradzi sobie z każdym jednorodnego układu elementów, które są zgodne Sortable (jeśli jest ograniczone do jednego rodzaju betonu, a następnie @dfri's answer ma zostałeś objęty) - wtedy Sorter nie musi mieć w pierwszej kolejności wartości associatedtype. Zamiast tego można po prostu utworzyć ogólną metodę sort(items:), która umożliwiłaby użycie typu Sorter.

Również jeśli twoja metoda sort(items:) nie wykorzystuje żadnego stanu instancji (nie ma tego w twoim przykładowym kodzie), możesz zrobić to jako static - i po prostu przekazać typy sorterów zamiast instancji.

Na przykład, protokół Sortable i BasicSortable wdrożeniowe:

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 
    } 
} 

Twój protokół Sorter i różne implementacje 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: >) 
    } 
} 

i wreszcie swoje DataHandler z użycia Przykład:

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

Dziękuję bardzo. Podoba mi się pomysł użycia metody statycznej i przekazanie typu zamiast instancji. Usunąłem deklarację Fogmeister

+0

@Fogmeister Cieszę się, że pomogłem :) – Hamish

+2

Schludny! Teraz rzeczywiście bardzo dokładna, łączna odpowiedź na pytanie OPs :) – dfri

Powiązane problemy