swift - sequence() 上的 compactMap 不懒惰?
问题描述
每隔一段时间,我就必须沿着响应者链向上走,才能到达一个已知类的实例。(出于问题的目的,请接受这一点。)我一直在使用 while 循环来执行此操作,但我想到使用它会更酷sequence()
,它可以像这样巧妙地表达响应者链本身:
let chain = sequence(first: someView as UIResponder) {$0.next}
这太棒了,因为到目前为止我们实际上还没有进行任何步行。该序列是惰性的,并且在我们开始请求元素之前不会执行匿名函数。为了证明这一点,让我用 print 语句检测该代码:
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
失去懒惰吗?有没有办法把它找回来?(或者有什么其他优雅的方式让我完全逃脱了?)
解决方案
问题本身不是compactMap
。有两个问题:
如果您希望序列
compactMap
延迟调用,则需要使用lazy
.看起来这
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 {
...
}
推荐阅读
- apache-superset - 如何更改apache超集中图表的标签颜色
- azure-resource-manager - 无法使用 Azure CLI 获取资源组 - Get-AzureRmResourceGroup 命令
- android - Android:如何将文件写入目录
- list - 列表中的偶数和奇数索引
- node.js - Socket.io 不断向每个客户端发出请求的数据
- html - 单击锚链接时固定标题下的内容
- r - 按多个组选择多个数据帧上的值
- javascript - 使用量角器单击按钮后未加载下一页
- c# - c#如何在按键后显示下一个WriteLine
- node.js - 如何在一个登录页面中创建两个授权用户(管理员、客户)?