Swift 中的懒惰

Laziness in Swift

这里为什么用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
    }
}

此扩展采用一个转换函数,该函数 returns 是一个可选的,returns 一个仅包含那些未转换为 nil 的值的数组

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

它避免了创建中间数组。

self.map(transform)

returns 包含转换结果的 array all 个序列元素,然后将遍历这些元素以构建 包含非 nil 元素的结果数组。

lazy(self).map(transform)

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

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

正如 Martin R 提到的那样,lazy() 避免了创建中间数组。但是,如果我比较函数在不同大小数组上的执行时间,您会发现 lazy() 比 "only" 快 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() 转换和执行得到 "delayed" 因此,如果您在最后一个元素之前跳出循环,则可以更有效地处理 "big" 和无限序列。