2015-11-25 19 views
9

TL; DR

Perché questo non funziona?Utilizzo di init() nella mappa()

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

dettagli

Una cosa davvero cool che mi piace a Swift è la capacità di convertire una raccolta di una cosa all'altra passando in un metodo init (assumendo una init() per esiste quel tipo).

Ecco un esempio di conversione di un elenco di tuple in istanze di ClosedInterval.

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

Che esempio si avvale anche del fatto che siamo in grado di passare una tupla di argomenti come un unico argomento fino a quando il tuple corrisponde lista degli argomenti della funzione.

Ecco un altro esempio, questa volta per convertire un elenco di numeri in istanze di stringa.

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

Sfortunatamente, il prossimo esempio non funziona. Qui sto cercando di dividere una stringa in una lista di stringhe a singolo carattere.

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

map() deve funzionare su un elenco di Character (e in effetti sono stato in grado di verificare in un parco giochi che Swift inferisce il tipo corretto di [carattere] qui di essere passati in map).

String definitivamente può essere istanziato da un Character.

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

E, cosa interessante, funziona se i caratteri sono ciascuno nel proprio array.

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

O l'equivalente:

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

So che avrei potuto fare questo:

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

Ma io sono specificamente cercando di capire perché "abcdefg".characters.map(String.init) non funziona (IMO questa sintassi è anche più leggibile ed elegante)

risposta

13

Riproduzione semplificata:

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

Questo perché String ha due inizializzatori che accettano uno Character:

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

Per quanto ne so, non c'è modo per disambiguare loro quando si utilizza l'inizializzatore come valore.

Per quanto riguarda (1...100).map(String.init), String.init si riferisce a Int -> String.Anche se ci sono due inizializzatori che accettano uno Int:

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

tipo generico è più debole di tipo esplicito. Quindi il compilatore sceglie uno stringInterpolationSegment: in questo caso. È possibile confermare che tramite il comando + fare clic su .init.

+1

Questa è una risposta eccellente. C'è un documento di riferimento (o anche un articolo) che descrive l'uso di '.init' in questo modo (ad esempio in map/flatMap, ecc.)? –

+1

È possibile disambiguare i due inizializzatori; usa 'String.init (_ :)' per riferirsi al primo. – Sweeper

Problemi correlati