Swift 使用';懒惰';
我不知道为什么会有歧义。(很抱歉没有在这里添加代码,因为代码太长了。) 我已经添加了Swift 使用';懒惰';,swift,lazy-evaluation,overloading,swift4,Swift,Lazy Evaluation,Overloading,Swift4,我不知道为什么会有歧义。(很抱歉没有在这里添加代码,因为代码太长了。) 我已经添加了前缀(u.maxLength)作为LazyDropWhileBidirectionalCollection的重载下标(位置)在LazyPrefixCollection上定义。然而,上面示例中的以下代码不应该是含糊不清的,但它是: print([0, 1, 2].lazy.drop(while: {_ in false}).prefix(2)[0]) // Ambiguous use of 'lazy' 我的理解
前缀(u.maxLength)
作为LazyDropWhileBidirectionalCollection
的重载<代码>下标(位置)在LazyPrefixCollection
上定义。然而,上面示例中的以下代码不应该是含糊不清的,但它是:
print([0, 1, 2].lazy.drop(while: {_ in false}).prefix(2)[0]) // Ambiguous use of 'lazy'
我的理解是,协议层次结构中较高的重载将被使用
根据编译器,它不能在两种类型中进行选择;即LazyRandomAccessCollection
和LazySequence
。(这没有意义,因为下标(位置)
不是LazySequence
的方法)lazydrandomAccessCollection
将是这里的逻辑选择
如果我删除下标,它会工作:
print(Array([0, 1, 2].lazy.drop(while: {_ in false}).prefix(2))) // [0, 1]
可能是什么问题?将最后一行更改为:
let x = [0, 1, 2]
let lazyX: LazySequence = x.lazy
let lazyX2: LazyRandomAccessCollection = x.lazy
let lazyX3: LazyBidirectionalCollection = x.lazy
let lazyX4: LazyCollection = x.lazy
print(lazyX.drop(while: {_ in false}).prefix(2)[0])
你可以注意到这个数组有4种不同的惰性构象——你必须是明确的。这里的线索太复杂和模糊了。通过删除元素可以看到这一点。特别是,删除最后一个下标:
let z = [0, 1, 2].lazy.drop(while: {_ in false}).prefix(2)
在此配置中,编译器希望键入z
作为lazyprefexcollection
。但这不能用整数来表示。我知道感觉应该是这样,但当前的编译器无法证明它。(见下文)因此您的[0]
失败。回溯还不足以从这个疯狂的迷宫中走出来。有太多具有不同返回类型的重载,编译器不知道您想要哪一种
但这一特殊情况得到了细微的修正:
print([0, 1, 2].lazy.drop(while: {_ in false}).prefix(2).first!)
也就是说,我绝对不会把编译器推得这么厉害。这对今天的斯威夫特来说太聪明了。特别是返回不同类型的重载在Swift中通常是个坏主意。当它们很简单的时候,是的,你可以不受惩罚。但是,当您开始将它们分层时,编译器没有足够强大的证明引擎来解决它。(也就是说,如果我们研究的时间足够长,我敢打赌它在某种程度上是模糊的,但诊断是误导性的。这是一种非常常见的情况,当你进入过度聪明的Swift。)
既然您(在评论中)描述了它,那么推理就简单明了了
LazyDropWhileCollection
不能有整数索引。索引订阅要求为O(1)。这就是索引
下标相对于其他下标的含义。(索引
下标还必须返回元素
类型或崩溃;它不能返回元素?
。这样就有了一个与键
分开的字典索引
)
由于集合是惰性的,并且缺少任意数量的元素,因此查找任何特定的整数“count”(第一个、第二个等)都是O(n)。如果不经过至少100个元素,就不可能知道第100个元素是什么。要成为一个集合,它的O(1)索引必须是一种只能通过先前遍历序列来创建的形式。它不能是Int
这一点很重要,因为当您编写以下代码时:
for i in 1...1000 { print(xs[i]) }
您希望它大约是1000个“步骤”,但是如果这个集合有一个整数索引,它将大约是100万个步骤。通过包装索引,它们首先会阻止您编写该代码
这在高度通用的语言(如Swift)中尤其重要,在Swift中,通用算法层可以轻松地将意外的O(n)操作级联到完全不可行的性能中(所谓“不可行”是指您希望花费毫秒或更多时间的事情).编译器输出显示了两个可能的
lazy
候选对象。请发布相关的“问题代码”?@shallow尽管相关问题代码是第一句中链接的最后一行。我已经在上面的帖子中添加了它。据我所知,协议链中较高的重载将被使用。根据编译器,它不能在两种类型中进行选择;即LazyRandomAccessCollection
和Sequence
。(这是没有意义的,因为下标(位置)
不是顺序
的方法)LazyRandomAccessCollection
在这种情况下是合乎逻辑的选择。谢谢您的回答。经过一些挖掘后,[0]
失败的原因可能与drop(while:)
(和prefix(while:)
)将底层索引包装在LazyDropWhileIndex
(和lazyprefix whileindex
)中有关。这是一个struct
,它有一个隐式init
(因此不能在标准库之外进行初始化)。符合LazyCollectionProtocol
的所有其他类型不包装其索引,而是“转发”基础基集合的索引。(我还没有完全理解包装的原因。)谢谢你的编辑。还有一些想法LazyPrefixWhileIndex
做了一个很酷的技巧,endIndex
忽略基本集合的索引,只返回一个enum
值,表示O(1)中的结束索引。为任何涉及LazyPrefixWhileIndex
的内容编写单元测试都是不可能的,因为它的init
是隐式的,而保存索引表示的底层值是内部的
。使用LazyDropWhileIndex
我们至少可以访问base
索引,这样我们就可以根据基本集合的索引类型的实例对其进行测试。总之,我可以理解LazyPrefixWhileIndex
背后的原因,但是对于LazyDropWhileIndex
来说,他们可以只使用底层的索引