2015-11-25 1 views
9

TL; DR

Warum funktioniert das nicht?Verwendung von init() in map()

"abcdefg".characters.map(String.init) // error: type of expression is ambiguous without more context 

Einzelheiten

Eine wirklich coole Sache, die ich in Swift mag die Fähigkeit, ist eine Sammlung von eins zum anderen, indem man in einem init-Methode (unter der Annahme eines init() für diese Art vorhanden ist) zu konvertieren.

Hier ist ein Beispiel, das eine Liste von Tupeln in Instanzen von ClosedInterval konvertiert.

[(1,3), (3,4), (4,5)].map(ClosedInterval.init) 

Dieses Beispiel auch den Vorteil der Tatsache nimmt, dass wir so lange ein Tupel von Argumenten als ein einziges Argument übergeben können als das Tupel der Argumentliste der Funktion übereinstimmt.

Hier noch ein Beispiel, dieses Mal eine Liste von Zahlen in String-Instanzen umwandeln.

(1...100).map(String.init) 

Leider funktioniert das nächste Beispiel nicht. Hier versuche ich eine Zeichenkette in eine Liste von Zeichenketten aufzuteilen.

"abcdefg".characters.map(String.init) // error: type of expression is ambiguous without more context 

map() sollte auf einer Liste von Character in Betrieb sein (und in der Tat war ich in der Lage in einem Spielplatz zu überprüfen, die Swift die richtige Art von [Zeichen] folgert hier in map geleitet wird).

String kann definitiv aus einer Character instanziiert werden.

let a: Character = "a" 
String(a) // this works 

Und interessanterweise funktioniert das, wenn die Zeichen jeweils in einem eigenen Array sind.

"abcdefg".characters.map { [$0] }.map(String.init) 

oder das Äquivalent:

let cx2: [[Character]] = [["a"], ["b"], ["c"], ["d"]] 
cx2.map(String.init) 

Ich weiß, dass ich dies tun könnte:

"abcdefg".characters.map { String($0) } 

Aber ich versuche, speziell zu verstehen, warum "abcdefg".characters.map(String.init) funktioniert nicht (IMO diese Syntax ist auch besser lesbar und elegant)

Antwort

13

Vereinfachte Repro:

String.init as Character -> String 
// error: type of expression is ambiguous without more context 

Dies liegt daran, String zwei initializers hat, die eine Character akzeptieren:

init(_ c: Character) 
init(stringInterpolationSegment expr: Character) 

Soweit ich weiß, gibt es keine Möglichkeit, sie eindeutig zu machen, wenn die Initialisierung als Wert verwendet wird.

Für (1...100).map(String.init) wird String.init als Int -> String bezeichnet.Obwohl es zwei initializers sind, die eine Int akzeptieren:

init(stringInterpolationSegment expr: Int) 
init<T : _SignedIntegerType>(_ v: T) 

gattungsgemäßen Art ist schwächer als explizite Typ. Der Compiler wählt also in diesem Fall stringInterpolationSegment:. Sie können dies durch Befehl + klicken Sie auf .init.

+1

Dies ist eine ausgezeichnete Antwort. Gibt es ein Referenzdokument (oder sogar einen Artikel), das die Verwendung von '.init 'auf diese Weise beschreibt (z. B. in map/flatMap usw.)? –

+1

Sie können die zwei Initialisierer disambiguieren; Verwenden Sie 'String.init (_ :)', um auf den ersten Bezug zu nehmen. – Sweeper