sequence() 上的 compactMap 不懒惰?

compactMap on sequence() not lazy?

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

let chain = sequence(first: someView as UIResponder) {[=10=].next}

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

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

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

if let vc = (chain.first {[=12=] is ViewController}) as? ViewController {
    print(vc)
}

打印输出显示惰性得以维持:我们沿着响应者链向上走,直到我们到达 ViewController 并停下来。完美的!在大括号内,vc 被输入为 ViewController,我们开始比赛了。

它不会逃过你的注意,但是,那是丑陋的。我正在测试和铸造。有没有一种方法可以在不进行测试的情况下进行投射,但最终仍会得到 ViewController?

这很优雅,而且工作正常:

for case let vc as ViewController in chain {
    print(vc)
    break
}

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

好吧,当我想到这个的时候,我真的充满了希望:

if let vc = (chain.compactMap{ [=14=] as? ViewController }.first) {
    print(vc)
}

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

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

  1. 如果你想让序列懒惰地调用compactMap,你需要使用lazy.

  2. 看来 first 正在阻止懒惰行为。但是,如果您使用 first(where:),您确实会喜欢这种懒惰的行为。

因此,虽然有些不雅,但以下内容可以满足您的需求:

if let vc = (chain.lazy.compactMap { [=10=] as? ViewController }.first { _ in true } ) {
    ...
} 

或者,如您所说,您可以在 Sequence 上实施 first(或 lazyFirst):

extension Sequence {
    var first: Element? {
        return first { _ in true }
    }
}

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

if let vc = chain.lazy.compactMap({ [=12=] as? ViewController }).first {
    ...
}