在 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:)
办法能够供给更好的功能。在实际运用中,应该依据具体情况挑选适宜的办法。