本文介绍了对sequence()的compactMap不偷懒吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Every once in a while I have to walk up the responder chain to reach an instance of a known class. (Just accept this for purposes of the question.) I've been doing this with a while loop, but it occurred to me that it would be cooler to use sequence(), which can express the responder chain itself neatly like this:

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

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

That's brilliant because so far we haven't actually done any walking; the sequence is lazy and the anonymous function won't be executed until we start asking for elements. To prove that, let me instrument that code with a print statement:

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

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

Okay, so let's say I'm looking for the first ViewController instance in the chain. I can find it like this:

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

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

The printout shows that laziness is maintained: we walked up the responder chain until we got to the ViewController and stopped. Perfect! Inside the curly braces, vc is typed as ViewController and we're off to the races.

这不会吸引您的注意,但是,这很丑陋.我正在测试和投放.有没有一种方法我可以在不进行测试的情况下进行投放,而最终仍然使用ViewController?

It will not have escaped your attention, however, that that's ugly. I'm both testing and casting. Is there a way I can just cast without testing and still end up with a ViewController?

这很优雅,可以正常工作:

This is elegant and it works fine:

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

这很可爱,而且保持了懒惰-但我必须记得在最后说break,这会破坏一切.

That's lovely and laziness is maintained — but I have to remember to say break at the end, which sort of ruins everything.

好的,所以当我想到这个时,我真的充满希望:

Okay, so I was really hopeful when I thought of this:

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

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

It works in the sense that it compiles and gets the right answer and looks nice, but I've lost laziness. The entire chain is being traversed. Does compactMap lose laziness? Is there a way to get it back? (Or is there some other elegant way that has completely escaped me?)

推荐答案

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

The issue is not compactMap, per se. There are two issues:

  1. 如果您希望序列懒惰地调用compactMap,则需要使用 lazy .

  1. If you want the sequence to call compactMap lazily, you need to use lazy.

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

It would appear that first is preventing the lazy behavior. If you use first(where:), though, you do enjoy the lazy behavior.

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

Thus, while somewhat inelegant, the following achieves what you’re looking for:

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

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

Or, as you say, you can implement first (or lazyFirst) on Sequence:

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

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

And then this more simplified rendition now is still lazy:

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

这篇关于对sequence()的compactMap不偷懒吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

11-01 20:07