降低
过滤,缩小和映射:
轻按一下即可转换收藏。
这些功能上的朋友,
您将清除的代码,
输入不可变,没有陷阱。
映射和过滤器引起了很多关注,但是我说减少是重要的一环。 或者,如果您对效率感兴趣,该怎么做:您可以使用reduce来构建地图和过滤器。 如果您只想学习一种,请选择减少。
功能朋友
Map具有对序列中的每个项目执行某些操作并收集结果的有用行为。
let words = ["hello", "there"]
let shoutyWords = words.map { $0.uppercased() }
// ["HELLO", "THERE"]
map
的输入是大小为n的序列,输出的大小也为n 。
接下来,使用过滤器可以确定序列中要保留(和丢弃)的项目。
let numbers = 0...10
let evenNumbers = numbers.filter { $0 % 2 == 0 }
// [0, 2, 4, 6, 8, 10]
输出大小将介于0到n之间,具体取决于要过滤的事物数量。
减少什么?
减少减少
像其他两个操作一样, reduce
接受一个序列作为输入,但是它的输出更为通用:它可以是您想要的任何东西!
对于循环的每一转, reduce
将先前的输出与当前项目一起传递。 这意味着您可以在迭代之间保留一些状态,并且可以根据需要构建输出。
对于第一次,您需要提供一个初始启动器“结果”值。 这意味着reduce
需要两个参数:
- 初始结果值。
- 产生下一个部分结果的闭包。
Reduce将部分结果和下一个值作为闭包的参数传递。
使用减少
reduce
的教科书示例是找到总数:
let primes = [3, 7, 31, 127]
let sum = primes.reduce(0) { (result, item) in
return result + item
}
第一次通过, reduce
将调用closure(0, 3)
,其中0是初始值,3是第一项。 现在的结果是0 + 3或3。
下一次通过循环, reduce
将调用closure(3, 7)
,其中3是上次的结果,而7是下一项。 等等。
最后,我们将把数组[Int]
转换为Int
类型的最终结果。
建筑减少
让我们看一下标准库中reduce
的构建方式。 和往常一样,代码将在下面内联,您可以在GitHub上的SequenceAlgorithms.swift中自己查看代码。
一旦获得了稍微粗糙的功能签名,它就很短了:
public func reduce(
_ initialResult: Result,
_ nextPartialResult:
(_ partialResult: Result, Iterator.Element) throws -> Result )
rethrows -> Result {
这里有很多Result
类型-这是描述最终返回值将是什么的通用类型。 这可能与Iterator.Element
不同,后者是序列中事物的类型。
initialResult
参数的类型为Result
,需要作为第一个参数传递到函数中。
第二个参数是针对序列中每个值运行的闭包。 闭包本身传递了两个参数:
- 从上一次迭代返回的结果。
- 序列中的下一项。
最后, throws
和rethrows
关键字允许闭包引发错误。 如果需要,这些将被重新链接到原始呼叫者。
累积结果
当我们遍历序列时, accumulator
变量将保存累加结果。
var accumulator = initialResult
我们将从传递给函数的初始结果开始。
接下来是重要的迭代步骤:
for element in self {
accumulator = try nextPartialResult(accumulator, element)
}
每次通过循环,累加器都会被闭包返回的新的部分值替换。 同样,这里的闭包采用上一个结果和下一个元素。
仅需一行:
return accumulator
最后,剩下要做的就是返回最后一个结果。
建筑图
如何使用reduce构建map
? 为简单起见,我们将使用数组而不是序列。
让我们从reduce
工作方式来考虑map
:
- 结果类型将是一个数组
- 初始值应为空数组
- 对于循环的每一圈,将一个项目添加到数组中
extension Array {
func mapUsingReduce(
_ closure:
(_ item: Iterator.Element) -> OutElement) -> [OutElement] {
return self.reduce([]) { (partial, item) in
return partial + [closure(item)]
}
}
}
通用参数OutElement
是输出数组中元素的类型。 该方法使用闭包来执行将Iterator.Element
转换为OutElement
的转换步骤。
reduce
调用在这里是预期的:从一个空数组开始,并在每次循环时都将其添加到累积数组中。
您能想到如何使用reduce
编写filter
吗? sort
怎么sort
? 您还能想到什么其他用途? 与实际map
这种方式做map
效率如何?
结束括号
在这种情况下,学习如何使用锤子意味着每个问题开始看起来都像钉子一样(纠结另一句话)。 每当我需要对数组进行某种操作时,我就开始考虑如何使用reduce
。
但这并不意味着我实际上使用了 reduce
并不是每个问题的最佳解决方案! 我喜欢它的原因是让我仔细考虑以下基础知识:初始值,增量步骤以及如何缓慢建立返回值。
下载 Reduce.playground 来签出代码并 亲自试用 。
}