序列()上的 compactMap 不懒惰?

mat*_*att 4 lazy-sequences swift

每隔一段时间,我必须沿着响应者链向上走才能到达已知类的实例。(只是为了这个问题接受这个。)我一直在用一个 while 循环来做这个,但我突然想到使用它会更酷sequence(),它可以像这样巧妙地表达响应者链本身:

let chain = sequence(first: someView as UIResponder) {$0.next}
Run Code Online (Sandbox Code Playgroud)

这太棒了,因为到目前为止我们还没有真正走路;序列是惰性的,在我们开始请求元素之前不会执行匿名函数。为了证明这一点,让我用打印语句检测该代码:

let chain = sequence(first: someView as UIResponder) {r in print(r); return r.next}
Run Code Online (Sandbox Code Playgroud)

好的,假设我正在寻找链中的第一个 ViewController 实例。我可以这样找到它:

if let vc = (chain.first {$0 is ViewController}) as? ViewController {
    print(vc)
}
Run Code Online (Sandbox Code Playgroud)

打印输出显示惰性仍然存在:我们沿着响应者链向上走,直到到达 ViewController 并停止。完美的!在花括号内,vc输入为 ViewController,我们就开始比赛了。

然而,它不会逃脱你的注意,那是丑陋的。我正在测试和铸造。有没有一种方法可以让我在不测试的情况下进行转换并且仍然以 ViewController 结束?

这是优雅的,它工作正常:

for case let vc as ViewController in chain {
    print(vc)
    break
}
Run Code Online (Sandbox Code Playgroud)

这很可爱,而且保持着懒惰——但我必须记得break在最后说,这会毁了一切。

好吧,当我想到这一点时,我真的充满希望:

if let vc = (chain.compactMap{ $0 as? ViewController }.first) {
    print(vc)
}
Run Code Online (Sandbox Code Playgroud)

它的工作原理是它可以编译并获得正确的答案并且看起来不错,但我已经失去了惰性。整个chain正在被遍历。会compactMap失去懒惰吗?有没有办法找回来?(或者是否有其他一些优雅的方式完全逃脱了我?)

Rob*_*Rob 6

问题compactMap本身不是。有两个问题:

  1. 如果您希望序列compactMap延迟调用,则需要使用lazy.

  2. 这似乎first阻止了懒惰的行为。first(where:)但是,如果您使用,您确实喜欢这种懒惰的行为。

因此,虽然有些不雅,但以下实现了您正在寻找的内容:

if let vc = (chain.lazy.compactMap { $0 as? ViewController }.first { _ in true } ) {
    ...
} 
Run Code Online (Sandbox Code Playgroud)

或者,正如你所说,你可以实现first(或lazyFirstSequence

extension Sequence {
    var first: Element? {
        return first { _ in true }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后这个更简化的再现现在仍然是懒惰的:

if let vc = chain.lazy.compactMap({ $0 as? ViewController }).first {
    ...
} 
Run Code Online (Sandbox Code Playgroud)

  • 我在 https://bugs.swift.org/browse/SR-5754 添加了评论,我相信它是相关的。 (3认同)