2015-11-25 2 views
9

TL; DR

Почему это не работает?Использование init() на карте()

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

Детали

Один действительно здорово, что я люблю в Swift является возможность преобразовать коллекцию одной вещи к другой, передавая в метод инициализации (предполагая init() для этого типа существует).

Вот пример преобразования списка кортежей в экземпляры ClosedInterval.

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

Этот пример также использует тот факт, что мы можем передать кортеж аргументов в качестве единственного аргумента, пока кортеж соответствует список аргументов функции.

Вот еще один пример, на этот раз преобразование списка чисел в строковые экземпляры.

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

К сожалению, следующий пример не работает. Здесь я пытаюсь разделить строку на список односимвольных строк.

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

map() должен работать в списке Character (и на самом деле я был в состоянии проверить на детской площадке, что Swift выводит правильный тип [Характер] здесь, передаваемой в map).

String определенно может быть создан из Character.

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

И, что интересно, это работает, если персонажи находятся в своем собственном массиве.

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

Или эквивалент:

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

Я знаю, что я мог бы сделать это:

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

Но я определенно пытаюсь понять, почему "abcdefg".characters.map(String.init) не работает (IMO этот синтаксис также более читабельны и изящны)

ответ

13

Упрощенный репродукция:

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

Это потому, что String имеет два инициализаторы, которые принимают один Character:

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

Насколько я знаю, нет никакого способа неоднозначности их при использовании инициализатора в качестве значения.

Что касается (1...100).map(String.init), String.init упоминается как Int -> String.Хотя есть два Инициализаторы, которые принимают один Int:

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

Generic тип слабее, чем явного типа. Поэтому в этом случае компилятор выбирает stringInterpolationSegment:. Вы можете подтвердить это командой + нажмите на .init.

+1

Это отличный ответ. Есть ли ссылочный документ (или даже статья), описывающий использование '.init' таким образом (например, в map/flatMap и т. Д.)? –

+1

Вы можете устранить два инициализатора; используйте 'String.init (_ :)' для обозначения первого. – Sweeper

Смежные вопросы