对sequence()的compactMap不偷懒吗?

时间:2019-06-26 23:31:00

标签: swift lazy-sequences

每隔一段时间,我必须走到响应者链上才能到达一个已知类的实例。 (出于问题的目的,只是接受它。)我一直在使用while循环来执行此操作,但是我发现使用sequence()会更酷,它可以像这样整洁地表示响应者链:

let chain = sequence(first: someView as UIResponder) {$0.next}

那是很棒的,因为到目前为止我们实际上还没有进行任何散步;该序列是惰性的,并且直到我们开始请求元素之前,匿名函数才会执行。为了证明这一点,让我用一个打印语句对代码进行检测:

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

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

if let vc = (chain.first {$0 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{ $0 as? ViewController }.first) {
    print(vc)
}

从某种意义上说,它可以编译并得到正确的答案,看起来不错,但我已经失去了懒惰。整个chain被遍历。 compactMap会变得懒惰吗?有办法找回它吗? (或者还有其他一些优雅的方法使我完全摆脱了?)

1 个答案:

答案 0 :(得分:1)

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

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

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

因此,尽管有些微不足道,但以下功能可以满足您的需求:

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

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

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

然后这种更简化的演绎现在仍然很懒:

if let vc = chain.lazy.compactMap({ $0 as? ViewController }).first {
    ...
}