2017-12-25 104 views
6

Tôi đã kiểm tra .lazy cho các chức năng trật tự cao và đã có một số lỗi biên dịch thú vị liên quan đến flatMap chức năng (và có thể những người khác)"Không thể chuyển đổi biểu hiện trở lại" trong flatMap với một biểu thức vô nghĩa bên

Ví dụ

 
let array = [1, 2, 3, 4, 5, 6] 

array 
    .flatMap { 
     print("DD") 
     return $0 // Cannot convert return expression of type 'Int' to return type 'String?' 
    } 
    .forEach { 
     print("SS") 
     print($0) 
} 

Bình luận ra một chút

 
array 
    .flatMap { 
//  print("DD") 
     return $0 
    } 
    .forEach { 
     print("SS") 
     print($0) 
} 

Và mọi thứ hoạt động .. thậm chí dụ thú vị hơn

 
array 
    .flatMap { 
     let z = $0 
     return $0 // Or return z - all is the same "Cannot convert return expression of type 'Int' to return type 'String?'" 
    } 
    .forEach { 
     print("SS") 
     print($0) 
} 

gì có thể gây ra hành vi đó?

Trả lời

4

Phương pháp flatMap(_:) trên hiện Sequence (tính Swift 4) có hai ý nghĩa khác nhau:

  • Có thể mất một transform đóng cửa mà trả về một tùy chọn T?, và nó sẽ trở lại a [T], lọc ra các kết quả nil (quá tải này is to be renamed đến compactMap(_:) trong phiên bản sau).

    public func flatMap<ElementOfResult>(
        _ transform: (Element) throws -> ElementOfResult? 
    ) rethrows -> [ElementOfResult]

  • Có thể mất một transform đóng cửa mà trả về một Sequence, và nó sẽ trả về một mảng chứa các nối của tất cả các chuỗi kết quả.

    public func flatMap<SegmentOfResult : Sequence>(
        _ transform: (Element) throws -> SegmentOfResult 
    ) rethrows -> [SegmentOfResult.Element]

Bây giờ, trong Swift 4, String trở thành một RangeReplaceableCollection (và do đó một Sequence).Vì vậy, Swift 3 mã mà đã làm điều này:

// returns ["foo"], as using the `nil` filtering flatMap, the elements in the closure 
// are implicitly promoted to optional strings. 
["foo"].flatMap { $0 } 

nay thực hiện điều này:

// returns ["f", "o", "o"], a [Character], as using the Sequence concatenation flatMap, 
// as String is now a Sequence (compiler favours this overload as it avoids the implicit 
// conversion from String to String?) 
["foo"].flatMap { $0 } 

Để duy trì khả năng tương thích nguồn, chuyên flatMap quá tải were added cho chuỗi:

//===----------------------------------------------------------------------===// 
// The following overloads of flatMap are carefully crafted to allow the code 
// like the following: 
// ["hello"].flatMap { $0 } 
// return an array of strings without any type context in Swift 3 mode, at the 
// same time allowing the following code snippet to compile: 
// [0, 1].flatMap { x in 
//  if String(x) == "foo" { return "bar" } else { return nil } 
// } 
// Note that the second overload is declared on a more specific protocol. 
// See: test/stdlib/StringFlatMap.swift for tests. 
extension Sequence { 
    @_inlineable // FIXME(sil-serialize-all) 
    @available(swift, obsoleted: 4) 
    public func flatMap(
    _ transform: (Element) throws -> String 
) rethrows -> [String] { 
    return try map(transform) 
    } 
} 

extension Collection { 
    @_inlineable // FIXME(sil-serialize-all) 
    public func flatMap(
    _ transform: (Element) throws -> String? 
) rethrows -> [String] { 
    return try _flatMap(transform) 
    } 
} 

như vậy rằng việc sử dụng ở trên vẫn sẽ trả lại một [String] trong Swift 3 chế độ tương thích, nhưng một [Character] trong Swift 4.

Vì vậy, tại sao

let array = [1, 2, 3, 4, 5, 6] 

array 
    .flatMap { 
     print("DD") 
     return $0 // Cannot convert return expression of type 'Int' to return type 'String?' 
    } 
    .forEach { 
     print("SS") 
     print($0) 
    } 

cho bạn biết rằng việc đóng cửa phải trả lại một String??

Vâng, Swift hiện không suy ra tham số và trả về các loại cho đóng nhiều câu lệnh (xem this Q&A để biết thêm thông tin). Vì vậy, quá tải flatMap(_:) khi quá trình đóng trả về hoặc là T? hoặc chung S : Sequence không đủ điều kiện để được gọi mà không có chú thích loại rõ ràng, vì chúng sẽ yêu cầu suy luận kiểu để đáp ứng các trình giữ chỗ chung.

Do đó, quá tải duy nhất đủ điều kiện, là khả năng tương thích nguồn đặc biệt String đặc biệt, vì vậy trình biên dịch dự kiến ​​việc đóng để trả lại String?.

Để sửa lỗi này, bạn rõ ràng có thể chú thích các kiểu trả về của việc đóng cửa:

array 
    .flatMap { i -> Int? in 
    print("DD") 
    return i 
    } 
    .forEach { 
    print("SS") 
    print($0) 
    } 

Nhưng nếu bạn không thực sự sử dụng chức năng lọc tùy chọn của flatMap(_:) quá tải này trong mã thực sự của bạn, bạn nên sử dụng map(_:) thay thế.

+1

Câu trả lời hay, cảm ơn bạn! Tôi đã sử dụng chức năng lọc tùy chọn của 'flatMap (_ :)' trong mã thực. Tôi vừa xóa một số mã để hiển thị sự cố mà không liên quan chặt chẽ đến các phần vấn đề .. –

4

flatMap có thể có ý nghĩa khác nhau tùy thuộc vào ngữ cảnh. Bạn có thể nói cho trình biên dịch chính xác hơn cái nào bạn đang định sử dụng.

enter image description here


Hai mục đích bình thường để áp dụng flatMap để một mảng mà trình biên dịch có thể suy ra là để

  • flatten mảng lồng nhau

    let array = [[1, 2, 3, 4, 5, 6], [7, 8, 9]] 
    let flattened = array.flatMap{$0} 
    print(flattened) // [1, 2, 3, 4, 5, 6, 7, 8, 9] 
    
  • bản đồ khác loại và lọc tùy chọn

    let array = ["1", "a", "2", "3", "b", "4", "5", "6"] 
    let flattened = array.flatMap{ Int($0) } 
    print(flattened) // [1, 2, 3, 4, 5, 6] 
    
+0

Vâng, tôi không nghĩ về việc chỉ định điều đó và sau khi tôi vừa thêm '(num) -> Int? in' mọi thứ hoạt động như mong đợi. Cảm ơn bạn :) –

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