2016-04-18 8 views
0

Rückkehr ...Wie ein Verfahren zur Herstellung eines generischen Swift 2.2 Protokolls implementieren, dass generisches Protokoll

Ich mag ein generisches Protokoll GBucket definieren, die ein bisschen ist wie eine Reihe von Elementen des gleichen Typs. Ein bisschen wie ein CollectionType, aber mit weit weniger Fähigkeiten. Wie so:

public protocol GBucket { 

    associatedtype BElement 

    func splitBucket 
    <A: GBucket, B: GBucket where A.BElement == Self.BElement, 
            B.BElement == Self.BElement> 
    (idx: Int) -> (A, B) 
} 

Es Wesentlichen bietet nur eine Methode ein GBucket in zwei neue GBucket s aufgeteilt. Das kann von jedem Typ sein, der mit dem Protokoll übereinstimmt - d. H. Die zurückgegebenen Teile müssen nicht die gleiche Klasse sein, die die Aufteilung durchführt.

Ich habe versucht, das als Beispielimplementierung:

extension ArraySlice : GBucket { 

    public typealias BElement = Generator.Element 

    public func splitBucket 
    <A: GBucket, B: GBucket where A.BElement == BElement, 
            B.BElement == BElement> 
    (idx: Int) -> (A, B) 
    { 
    let ls : ArraySlice<A.BElement> = self[0..<idx] 
    let a : A = ls // this conversion FAILs 
    return (a, self[idx..<self.count]) // this too, of course ;-) 
    } 
} 

Dies erzeugt:

Wert kann nicht vom Typ umwandeln 'ArraySlice < Element>' in dem angegebenen Typ 'A'

Was soweit ich sagen kann sollte sich gut konvertieren. ArraySlice ist ein GBucket und der Elementtyp ist der gleiche dank der where Spezifikation.

Und eine andere, kürzere Probe das Problem darstellt, das nicht Array Sachen nicht verwendet:

public protocol GBucket { 
    associatedtype BElement 
    func otherBucket<A: GBucket where A.BElement == Self.BElement>() -> A 
} 

public class MyBucketType<T> : GBucket { 
    public typealias BElement = T 

    public func otherBucket<A: GBucket where A.BElement == BElement>() ->  A { 
    return MyBucketType<A.BElement>() 
    } 
} 

Was mir falsch doin?

Antwort

-1

Das ArraySlice entspricht GBucket und A entspricht GBucket bedeutet nicht, dass Sie zwischen ihnen wechseln können. Sie können von spezielleren Typen in allgemeinere Typen umwandeln, jedoch nicht zwischen zwei anderen, spezielleren Typen.

Vielleicht könnte der folgende Code Ihr Problem lösen?

public protocol GBucket : CollectionType { 
    func splitBucket(idx: Int) -> (Self, Self) 
} 

extension ArraySlice : GBucket { 
    public func splitBucket(idx: Int) -> (ArraySlice, ArraySlice) 
    { 
     let A = self[0..<idx] 
     let B = self[idx..<self.count]  
     return (A, B) 
    } 
} 
+0

„Sie können von speziellere Typen gegossen allgemeinere Typen“ - soweit ich das sehe ich nicht. A * ist auf "BElement" durch die 'where'-Klausel auf genau denselben Typ spezialisiert - was genau meine Absicht ist. – hnh

+0

Ihre Antwort fehlt meine Anforderung "die zurückgegebenen Teile müssen nicht die gleiche Klasse sein, die den Split macht". I.e. Dies soll gelten: 'extension _ArrayType: GBucket {func splitBucket() -> (ArraySlice, ArraySlice)'. Das heißt, die Split-Funktion kann jede Art von "GBucket" zurückgeben, solange sie dem Elementtyp entspricht. Mit Ihrem Ansatz konnte _ArrayType nur _ArrayType und nicht ArraySlices zurückgeben. – hnh

+0

Ok, um Ihre splitBucket () -Funktion zu bekommen, um Objekte vom Typ A und B zurückzugeben, muss es eine Methode haben, um diese Objekte zu initialisieren, momentan gibt es keine Methode, dies zu ermöglichen. –

0

geschrieben Rasmus einen entsprechenden Link in einem Kommentar, die schön erklärt, warum der Ansatz in der Frage nicht: Swift Types - Returning constrained generics from functions and methods. Er hat seine Antwort nicht geändert, daher stelle ich eine zur Verfügung - es ist wirklich seine ;-)

Der Schlüsselpunkt ist, dass Generika auf der Call-Site aufgelöst werden. Der generische Teil ist eher ein C-Makro. Konzeptionell gibt es nichts Dynamisches daran - es ist wirklich ein "Ersetze irgendeinen generischen Typ durch einen tatsächlichen Typ".

Blick auf diese:

extension ArraySlice : GBucket { 

    public typealias BElement = Generator.Element 

    public func splitBucket 
    <A: GBucket, B: GBucket where A.BElement == BElement, 
            B.BElement == BElement> 
    (idx: Int) -> (A, B) 
    { 
    let ls : ArraySlice<A.BElement> = self[0..<idx] 
    let a : A = ls // this conversion FAILs 
    return (a, self[idx..<self.count]) // this too, of course ;-) 
    } 
} 

Die Typen A und B nur mit einem Typ ersetzt werden, wenn die splitBucket Methode (oder spezialisiert auf andere Weise) genannt wird! Beispiel:

let aSlice : ArraySlice<Int> = myArray[1..<5] 
let result : (Array<Int>, Array<Int>) = aSlice.splitBucket(3) 

Die Typen A und B in splitBucket würden Array<Int> an dieser Stelle nur erweitert werden. I.E.die spezialisiert (Generika erweitert) Version der Funktion wäre:

public func splitBucket(idx: Int) -> (Array<Int>, Array<Int>) { 
    let ls : ArraySlice<A.BElement> = self[0..<idx] 
    let a : Array<Int> = ls // this conversion FAILs 
    return (a, self[idx..<self.count]) // this too, of course ;-) 
} 

Aus diesem Grund ist die let a zum Scheitern verurteilt hat.

PS: Dies gilt nicht beantworten, wie das Ziel zu erreichen, aber es ist ein Ausgangspunkt :-)

Verwandte Themen