2015-11-23 15 views
7

Sorry für den generischen Titel, es ist schwer, das Problem ohne Beispiele zu beschreiben.Was passiert mit dieser generischen Funktion?

nehme ich definieren die folgende generische Funktion, die auf Equatable Typen beschränkt ist:

func test<T: Equatable>(expect expected: T, run:() -> T) { 
    let value = run() 
    if value == expected { 
     print("OK") 
    } else { 
     print("Expected: \(expected), Actual: \(value)") 
    } 
} 

Hier ist ein Beispiel der Funktion unter Verwendung der:

test(expect: 100) { 10 * 10 } // prints "OK" 
test(expect: 1000) { 10 * 10 } // prints "Expected: 1000, Actual: 100" 

Und natürlich kann ich speichern die Wert anstelle von Literalen:

let e = 100 
test(expect: e) { e } // prints "OK" 

So weit so gut, alles funktioniert wie erwartet (kein Wortspiel beabsichtigt).

Nun wollen wir versuchen, diese mit einem Array:

test(expect: [1, 2]) { [1, 2] } // prints "OK" 

Wieder einmal die Dinge funktionieren.

Aber wir dies jetzt versuchen:

let a = [1, 2] 
test(expect: a) { a } // error: cannot convert value of type '() -> [Int]' to expected argument type '() -> _' 

So ist die Frage, die ich aufgebaut habe, ist: Warum funktioniert das nicht?

Spielplatz folgert richtig die Art der a[Int] zu sein, so wo kommt die Erwartung () -> _ kommen aus?

Der Versuch, eine Reihe von Variationen des letzten Beispiel:

test(expect: a) { return a } 
test(expect: a) { return a as [Int] } 
test(expect: a as [Int]) { return a as [Int] } 
test(expect: [1, 2]) { a } 
test(expect: [1, 2] as [Int]) { a } 

Sie alle Ergebnisse in dem gleichen Problem. Aus irgendeinem Grund scheint Swift zu glauben, dass die Funktion () -> _ erwartet.

Also vielleicht ist es nur, weil Arrays sind nicht Equatable, aber dies funktioniert:

let a = [1, 2] 
[1, 2] == [1, 2] 
a == a 

Ich dachte, ich Generika verstanden recht gut, und ich bin von dieser völlig ratlos. Ist das ein Fehler in Swift oder ein Fehler in meiner Definition von test()? Kann das Ziel überhaupt erreicht werden?

Die Lösung

Dank @ Sulthan Antwort unten, konnte ich eine andere Version dieser Funktion schreiben, den Array-Fall (und jeden SequenceType für diese Angelegenheit) zu handhaben:

public func test<T: SequenceType where T.Generator.Element: Equatable>(expect expected: T, run:() -> T) { 
    let result = run() 
    // Note: zip() will stop at the shorter array, so this implementation isn't correct, don't use it (it will incorrectly end up saying [1] == [1,2]). This code is just here to demonstrate the function's generic constraint. 
    let eq = zip(expected, result).filter(!=).isEmpty 
    if eq { 
     print("OK") 
    } else { 
     print("Expected: \(expected), Actual: \(result)") 
    } 
} 

let a: [Int] = [1, 2] 
test(expect: [1,2]) { a } // prints "OK" 
test(expect: [1,3]) { a } // prints "Expected: [1, 3], Actual: [1, 2]" 
+2

Die Antwort ist die gleiche wie für http://StackOverflow.com/A/33732669/669586, aber ich bin mir nicht sicher, ob ich diese als ein Duplikat schließen sollte. – Sulthan

+1

@Sultan Es ist definitiv die gleiche Frage an der Wurzel, aber die Tatsache, dass dies durch eine Funktion zu tun, verschleiert das Problem ein wenig, denke ich, macht es würdig, auf eigene Faust zu stehen. Außerdem war ein Teil der Frage, wie man diese Arbeit macht, die der Schnitt auf die Frage auch anspricht. – vopilif

+0

Als eine Randnotiz über meine "Lösung" oben. Es ist wahrscheinlich eine schlechte Idee, die Implementierung von 'test()' auf einen 'SequenceType' zu ​​beschränken, da es unendlich viele Sequenzen haben kann, so dass Sie nicht versuchen wollen, über alle zu iterieren. 'CollectionType' wäre besser geeignet. – vopilif

Antwort

5

Arrays don Passt nicht automatisch zu Equatable, auch wenn ihre Werte Equatable sind. Wenn Sie jedoch direkt ein Array-Literal verwenden, versucht der Compiler, den Typ zu vergleichen und konvertiert das Array in eine NSArray, die Equatable entspricht.

+0

Das ist es! Wow, das nervt mich schon seit einiger Zeit! Vielen Dank! – vopilif

+1

Eigentlich spielen Sie mit diesem etwas mehr .. Sind Sie sicher, dass dies zu einem NSArray konvertiert? Wenn ich 'import Foundation 'entferne, kann ich NSArray nicht mehr verwenden, aber' [1,2,3] == [1,2,3] 'funktioniert immer noch, also muss etwas anderes los sein. – vopilif

+1

@vopilif Es gibt '==' definiert für Arrays mit 'Element: Equatable'. Es bedeutet nicht, dass Array "Equatable" implementiert. – Sulthan