Swift 中怎样更快地 reduce
在 Swift 中,对于集合类型,Swift 标准库提供了若干方便的方法,可以对数据进行处理,其中一个比较常见的就是 reduce。reduce 这个单词,通过查阅字典,可以发现其有“简化、归纳”的意思,也就是说,可以用 reduce 把一组数据归纳为一个数据,当然这个一个数据也可以是一个数组或任何类型。
比较常见的 reduce 使用案例,例如:
求和:
let numbers = [1, 2, 3, 4, 5]
let sum = numbers.reduce(0, +)
print(sum) // 输出 15
字符串拼接:
let words = ["hello", "world", "how", "are", "you"]
let sentence = words.reduce("", { $0 + " " + $1 })
print(sentence) // 输出 " hello world how are you"
两个 reduce API
观察 reduce 方法的声明,会发现有两个不同的 API,一个是 reduce
一个是 reduce(into:)
,他们的功能是一样的,但是却略有不同。
reduce
方法的函数签名如下:
func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
该方法接收一个初始值和一个闭包作为参数,该闭包将当前的结果值和集合中的下一个元素作为输入,并返回一个新的结果值。reduce 方法依次迭代集合中的每个元素,并根据闭包的返回值更新结果值,最终返回最终结果值。
还是回到最简单的求和上来,下面的代码使用 reduce
方法计算一个数组中所有元素的总和:
let numbers = [1, 2, 3, 4, 5]
let sum = numbers.reduce(0, { $0 + $1 })
print(sum) // 输出 15
而 reduce(into:)
方法的函数签名如下:
func reduce<Result>( into initialResult: __owned Result, _ updateAccumulatingResult: (inout Result, Element) throws -> Void ) rethrows -> Result
该方法接收一个初始值和一个闭包作为参数,该闭包将当前的结果值和集合中的下一个元素作为输入,并使用 inout
参数将更新后的结果值传递回去。reduce(into:) 方法依次迭代集合中的每个元素,并根据闭包的返回值更新结果值,最终返回最终结果值。
下面的代码使用 reduce(into:)
方法计算一个数组中所有元素的总和:
let numbers = [1, 2, 3, 4, 5]
let sum = numbers.reduce(into: 0, { result, element in
result += element
})
print(sum) // 输出 15
可以看到,reduce(into:)
方法中闭包的参数使用了 inout
关键字,使得闭包内部可以直接修改结果值。这样可以避免不必要的内存分配和拷贝,因此在处理大量数据时,使用 reduce(into:)
方法可以提高性能。
观察源码
我们再通过观察源码证实这一结论
reduce
方法的源码实现如下:
public func reduce<Result>(
_ initialResult: Result,
_ nextPartialResult: (Result, Element) throws -> Result
) rethrows -> Result {
var accumulator = initialResult
for element in self {
accumulator = try nextPartialResult(accumulator, element)
}
return accumulator
}
可以发现这里有两处拷贝,一处是在 accumulator
传参给 nextPartialResult
时,一处是在把 nextPartialResult
的结果赋值给 accumulator
变量时,由于这里的 accumulator
的类型是一个值类型,每次赋值都会触发 Copy-on-Write
中的真正的拷贝。并且这两处拷贝都是在循环体中,如果循环的次数非常多,是会大大拖慢性能的。
再看 reduce(into:)
方法的源码:
func reduce<Result>(
into initialResult: __owned Result,
_ updateAccumulatingResult: (inout Result, Element) throws -> Void
) rethrows -> Result {
var result = initialResult
for element in self {
try updateAccumulatingResult(&result, element)
}
return result
}
在方法的实现中,我们首先将 initialResult
复制到一个可变变量 result
中。然后,我们对序列中的每个元素调用 updateAccumulatingResult
闭包,使用 &
语法将 result
作为 inout
参数传递给该闭包。因此这里每次循环都是在原地修改 result 的值,并没有发生拷贝操作。
总结
因此,reduce
方法和 reduce(into:)
方法都可以用来将集合中的元素组合成单个值,但是对于会触发 Copy-on-Write
的类型来说, reduce(into:)
方法可以提供更好的性能。在实际使用中,应该根据具体情况选择合适的方法。
链接:https://juejin.cn/post/7219712310586982457
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。