简体   繁体   中英

Lazily Mapping an Array Using a Throwing Closure

When lazily mapping an array of values, I receive an instance of type LazyMapSequence as expected:

Welcome to Apple Swift version 5.7 (swiftlang-5.7.0.127.4 clang-1400.0.29.50).
Type :help for assistance.
  1> let numbers = Array(1...5)
numbers: [Int] = 5 values {
  [0] = 1
  [1] = 2
  [2] = 3
  [3] = 4
  [4] = 5
}
  2> let squares = numbers.lazy.map { $0 * $0 }
squares: LazyMapSequence<LazySequence<[Int]>.Elements, Int> = {
  _base = 5 values {
    [0] = 1
    [1] = 2
    [2] = 3
    [3] = 4
    [4] = 5
  }
  _transform =
}

However, if the map(_:) method receives a throwing a closure instead, the mapping is not performed lazily, and I receive an array instead:

  3> func square(_ x: Int) throws -> Int {
  4.     return x * x
  5. }
  6> let squares = try numbers.lazy.map(square)
squares: [Int] = 5 values {
  [0] = 1
  [1] = 4
  [2] = 9
  [3] = 16
  [4] = 25
}

Why is that, and how do I lazily map an array of values using a throwing closure?

A workaround is

extension LazySequennce {
    func tryMap<U>(_ transform: @escaping (Self.Element) throws -> U) -> LazyMapSequence<Self.Elements, Result<U, Error>> {
        self.map { x in Result(catching: { try transform(x) }) }
    }
}

Note that the element type of the sequence is Result<U, Error> . We essentially "catch" the error whenever any is thrown. The error has to be caught because when iterating over any Sequence , the protocol requires that no errors are thrown.

As for why map(square) is not lazy, it is exactly as you have observed. LazySequenceProtocol.map takes a closure that does not throw.

func map<U>(_ transform: @escaping (Self.Element) -> U) 
    -> LazyMapSequence<Self.Elements, U>

When you pass in the throwing method, it instead calls Sequence.map :

func map<T>(_ transform: (Self.Element) throws -> T) rethrows -> [T]

which is not lazy.

This would all be solved if there were a method that looked like:

func tryMap<U>(_ transform: @escaping (Self.Element) throws -> U) 
    -> LazyThrowingMapSequence<Self.Elements, U>

However, such a LazyThrowingMapSequence type cannot conform to Sequence , because its iterator cannot conform to IteratorProtocol . Its iterator's next method throws , but IteratorProtocol requires that next does not throw.

It is theoretically possible to write LazyThrowingMapSequence by just adding throws to a few places to LazyMapSequence . ( Source code of LazyMapSequence is here ) But using it would be a pain, since you cannot iterate over it with a for loop, and it doesn't have any of those convenient methods from the Sequence protocol.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM