企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
### 别样的求和方式:sumBy、sum、fold、reduce 在**开发中,对集合进行求和操作是十分常见的操作**。比如我们要得到该列表中学生的平均分就需要先计算出所有学生的总分。 ``` var scoreTotal = 0 for (item in students) { scoreTotal = scoreTotal + item.score } ``` 上面就是我们常用的一种求和方式。在Kotlin中,我们可以这样去做: ``` val scoreTotal = students.sumBy {it.score} ``` 我们调用了Kotlin集合中的sumBy方法实现了求和,省去了一些多余的步骤。其实在Kotlin的集合中还有许多求和的API,接下来我们就来了解一下。 #### 1. sum:对数值类型的列表进行求和 sum与sumBy类似,也是一个比较常见的求和API,但是它**只能对一些数值类型的列表进行求和**。比如: ``` val a = listOf(1, 2, 3, 4, 5) val b = listOf(1.1, 2.5, 3.0, 4.5) val aTotal = a.sum() val bTotal = b.sum() ``` 上面的**两个数值类型的列表就可以通过sum进行求和**。当然我们同样也可以使用sumBy: ``` val aTotal = a.sumBy {it} val bTotal = b.sumBy {it} ``` #### 2. fold fold方法是一个非常强大的API,我们先来看看实现它的源码: ~~~ public inline fun <T, R> Iterable<T>.fold(initial: R, operation: (acc: R, T) -> R): R { var accumulator = initial for (element in this) accumulator = operation(accumulator, element) return accumulator } ~~~ 可以看到,fold方法需要接收两个参数,第1个参数initial通常称为初始值,第2个参数operation是一个函数。**在实现的时候,通过for语句来遍历集合中的每个元素,每次都会调用operation函数,而该函数的参数有两个,一个是上一次调用该函数的结果(如果是第一次调用,则传入初始值initial),另外一个则是当前遍历到的集合元素。简单来说就是:每次都调用operation函数,然后将产生的结果作为参数提供给下一次调用**。 那我们先来使用一下这个方法(紧接着filter函数章节的person数据类): ~~~ val scoreTotal = persons.fold(0) { accumulator, person -> accumulator + person.score } println("所有人的总分:$scoreTotal") ~~~ 输出结果 ``` 所有人的总分:447 ``` 通过上面的方式我们同样也能得到所有学生的总分。在上面的代码中,**fold方法接收一个初始值0,然后接收了一个函数,也就是后面的Lambda表达式**。 ``` { accumulator, person -> accumulator + person.score } ``` 上面的函数有两个参数,第1个参数为每次执行该函数后的返回结果,第2个参数为学生列表中的某个元素。我们通过让前一次执行之后的结果与当前遍历的学生的分数相加,就实现了求和的操作。简单说来,上面的方法的效果等价于下面的操作: ``` var accumulator = 0 for (person in persons) accumulator = accumulator + person.score ``` 其实就是一个简单的累加操作。同样我们还可以实现集合对第1个到最后一个元素的操作结果进行累乘: ~~~ val list = listOf(1, 2, 3, 4, 5) val total = list.fold(1) { mul, item -> mul * item } println(total)//120 ~~~ **fold很好地利用了递归的思想**。除了以上简单的操作之外,其实它还有非常强大的作用。在本书的第10章中我们会用fold结合函数式数据结构来展示这一特点。 #### 3. reduce reduce方法和fold非常相似,**唯一的区别就是reduce方法没有初始值**。我们同样来看看reduce方法的源码: ~~~ public inline fun <S, T : S> Iterable<T>.reduce(operation: (acc: S, T) -> S): S { val iterator = this.iterator() if (!iterator.hasNext()) throw UnsupportedOperationException("Empty collection can't be reduced.") var accumulator: S = iterator.next() while (iterator.hasNext()) { accumulator = operation(accumulator, iterator.next()) } return accumulator } ~~~ 可以发现,reduce方法只接收一个参数,该参数为一个函数。具体的实现方式也与fold类似,不同的是当要遍历的集合为空时,会抛出一个异常。因为没有初始值,所以默认的初始值是集合中的第1个元素。采用reduce方法同样能实现上面的求和操作(紧接着filter函数章节的person数据类): ~~~ val scoreTotal = persons.reduce {accumulator, person -> accumulator + person.score}//这里有问题 ~~~ **reduce方法和fold方法相似,当我们不需要初始值的时候可以采用reduce方法**。