2015-11-25 21 views
9

TL; DR

Tại sao tính năng này không hoạt động?Sử dụng init() trong bản đồ()

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

Chi tiết

Một điều thực sự mát mẻ Tôi thích ở Swift là khả năng chuyển đổi một bộ sưu tập của một điều khác bằng cách đi qua trong một phương thức init (giả định một init() cho loại đó tồn tại).

Dưới đây là ví dụ chuyển đổi danh sách các bộ dữ liệu thành các phiên bản ClosedInterval.

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

Ví dụ đó cũng lợi dụng thực tế là chúng tôi có thể chuyển một đối số làm đối số duy nhất miễn là tuple khớp với danh sách đối số của hàm.

Đây là một ví dụ khác, lần này chuyển đổi danh sách các số thành các thể hiện chuỗi.

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

Thật không may, ví dụ tiếp theo không hoạt động. Ở đây tôi đang cố gắng chia nhỏ một chuỗi thành một danh sách các chuỗi ký tự đơn.

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

map() nên được hoạt động trên một danh sách các Character (và thực sự tôi đã có thể xác minh trong một sân chơi mà Swift suy luận đúng loại [Character] ở đây được thông qua vào map).

String chắc chắn có thể được khởi tạo từ Character.

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

Và thú vị, điều này hoạt động nếu các ký tự nằm trong dãy của riêng chúng.

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

Hoặc tương đương:

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

Tôi biết rằng tôi có thể làm điều này:

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

Nhưng tôi đặc biệt cố gắng tìm hiểu tại sao "abcdefg".characters.map(String.init) không hoạt động (IMO cú pháp này là cũng dễ đọc và thanh lịch hơn)

Trả lời

13

Đơn giản hóa repro:

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

Điều này là do String có hai initializers chấp nhận một Character:

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

Theo như tôi biết, không có cách nào để disambiguate họ khi sử dụng khởi tạo như một giá trị.

Đối với (1...100).map(String.init), String.init được gọi là Int -> String.Mặc dù có hai initializers chấp nhận một Int:

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

Generic loại là yếu hơn loại rõ ràng. Vì vậy, trình biên dịch chọn stringInterpolationSegment: một trong trường hợp này. Bạn có thể xác nhận điều đó bằng cách lệnh + nhấp vào .init.

+1

Đây là câu trả lời tuyệt vời. Có tài liệu tham chiếu (hoặc thậm chí một bài báo) mô tả bằng cách sử dụng '.init' theo cách này (ví dụ: trong bản đồ/flatMap, v.v ...) không? –

+1

Bạn có thể phân biệt hai initialisers; sử dụng 'String.init (_ :)' để tham chiếu đến cái đầu tiên. – Sweeper

Các vấn đề liên quan