【问题标题】:Laziness in SwiftSwift 中的懒惰
【发布时间】:2015-07-05 14:35:57
【问题描述】:

这里为什么使用lazy

extension SequenceType {
    func mapSome<U>(transform: Generator.Element -> U?) -> [U] {
        var result: [U] = []
        for case let x? in lazy(self).map(transform) {
            result.append(x)
        }
        return result
    }
}

这个扩展接受一个转换函数,该函数返回一个可选值,并返回一个仅包含未转换为 nil 的值的数组

为什么不直接使用 self.map(transform) ?这里需要懒惰吗?

【问题讨论】:

  • 顺便说一句,flatMap&lt;U&gt;(transform: Generator.Element -&gt; U?) -&gt; [U] 现在可以在 Swift 2 标准库中使用 :)

标签: swift lazy-evaluation swift2


【解决方案1】:

它避免了中间数组的创建。

self.map(transform)

返回一个数组,其中包含转换的结果 所有序列元素,然后将被遍历以构建 结果数组包含非零元素。

lazy(self).map(transform)

是转换后元素的序列,然后 迭代以获得非零元素。转换后的元素 在枚举期间计算。 (每次拨打next() 在惰性序列上,通过转换下一个元素产生一个元素 原始序列的元素。)

这两种方法都有效。惰性方法可能会执行得更好 对于大序列,但这可能取决于许多因素(大小 数组的元素是值类型还是引用类型, 复制数组元素等的成本有多大)。对于小型阵列 由于额外的,惰性方法可能会更慢 高架。在一个具体的应用程序中,使用 Instruments 进行分析会 帮助决定使用哪种方法。

【讨论】:

  • 我的(当然是非正式的)性能测试表明惰性和非惰性对于小型数组的性能相同,但是惰性确实在较大的数组上具有适度的优势,因此值得包含(尤其是在库中)像这样的功能)。有趣的是,flatMap,从 2.0 开始执行相同的逻辑,但性能比两者都差。
  • @AirspeedVelocity:这很有趣,感谢您的反馈。
  • 有趣的是,我得到了相反的结果!?请参阅下面的答案。
【解决方案2】:

正如 Martin R 提到的,lazy() 避免了创建中间数组。但是,如果我比较函数在不同大小的数组上的执行时间,您会发现 lazy()“仅”快 10%。

有趣的是,您会发现 lazy() 适用于少于 200 个元素的数组,其速度最多可达到 2 倍,并且获得更多元素的速度几乎与没有转换的函数一样快(快 10%)。

(使用 Xcode 6.4 和 Xcode 7 测试,在 Playground 中作为(编译)源文件使用全局函数和协议扩展)

所以lazy() 宁愿用于Sequences 你不知道它是否是有限的。那么,for 循环很可能与breakreturn 一起使用:

for element in lazy(sequence).map{ ... } {
    if element == 1000 {
        break
    }
    // use element
}

如果您在无限 Sequence(如 1,2,3...)上调用 map,则执行也将是无限的。使用lazy(),转换和执行会“延迟”,因此如果您在最后一个元素之前跳出循环,您可以更有效地处理“大”和无限序列。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-16
    • 1970-01-01
    相关资源
    最近更新 更多