如何用优雅的Kotlin函数式编程技巧解决连续驾车行程里程最大值问题
Kotlin函数式解法:找出连续驾车行程链的最大里程总和
需求回顾
给定包含驾车(isCar=true)与非驾车行程的Journey列表,需找出连续2次及以上的驾车行程链,计算每个链的里程总和,最终输出最大值。
数据类定义:
data class Journey( val isCar: Boolean = false, val mileage: Int, )
示例输入(正确结果为80):
listOf( Journey(true, 10), Journey(true, 20), Journey(false, 105), Journey(true, 1046), Journey(false, 130), Journey(true, 40), Journey(true, 40), )
优雅函数式实现(无可变集合)
以下提供两种纯函数式解法,均避免使用可变集合,符合现代Kotlin编程风格。
解法一:单次遍历的fold实现(高效)
通过fold单次遍历维护状态,避免多次迭代,效率最优:
fun main() { val journeys = listOf( Journey(true, 10), Journey(true, 20), Journey(false, 105), Journey(true, 1046), Journey(false, 130), Journey(true, 40), Journey(true, 40), ) val maxConsecutiveMileage = journeys.fold(Triple(0, 0, emptyList<Int>())) { (currentCount, currentSum, validSums), journey -> when { journey.isCar -> { // 累加连续驾车的次数和里程 Triple(currentCount + 1, currentSum + journey.mileage, validSums) } else -> { // 遇到非驾车行程,若之前连续驾车次数≥2,记录总和 val updatedSums = if (currentCount >= 2) validSums + currentSum else validSums Triple(0, 0, updatedSums) } } }.let { (finalCount, finalSum, validSums) -> // 处理遍历结束后剩余的连续驾车链 val allValidSums = if (finalCount >= 2) validSums + finalSum else validSums allValidSums.maxOrNull() ?: 0 // 无符合条件的链时返回0,可按需改为null } println(maxConsecutiveMileage) // 输出80 }
思路说明:
- 用
Triple作为累加器,保存三个状态:当前连续驾车的次数、当前里程总和、已收集的符合条件的链总和 - 遍历每个行程时,根据是否为驾车行程更新状态:
- 驾车行程:累加次数与里程
- 非驾车行程:若之前连续次数达标,将总和存入有效列表,重置状态
- 遍历结束后检查最后一段连续链,再取最大值
解法二:分组式实现(直观简洁)
通过分组连续的行程类型,逻辑更直观:
fun main() { val journeys = listOf( Journey(true, 10), Journey(true, 20), Journey(false, 105), Journey(true, 1046), Journey(false, 130), Journey(true, 40), Journey(true, 40), ) val maxConsecutiveMileage = journeys // 生成连续行程的分组编号 .runningFold(Pair(0, false)) { (groupNum, prevIsCar), journey -> if (journey.isCar == prevIsCar) Pair(groupNum, journey.isCar) else Pair(groupNum + 1, journey.isCar) } .drop(1) // 移除初始的默认分组 .zip(journeys) // 将分组编号与原行程绑定 .groupBy({ it.first }, { it.second }) // 按分组编号聚合行程 .values // 提取所有分组 .filter { group -> group.all { it.isCar } && group.size >= 2 } // 筛选符合条件的驾车链 .maxOfOrNull { group -> group.sumOf { it.mileage } } ?: 0 // 计算最大总和 println(maxConsecutiveMileage) // 输出80 }
思路说明:
- 用
runningFold为连续相同类型的行程生成统一分组编号 - 通过
zip和groupBy将行程按连续类型分组 - 过滤出全为驾车且长度≥2的分组,计算每组总和后取最大值
原有代码的问题分析
- 效率低下:多次遍历列表(
filter、map、forEach+while循环),且indexOf可能因重复元素导致索引错误 - 可变集合依赖:使用多个
mutableListOf,不符合函数式无副作用的原则 - 逻辑复杂:嵌套循环和条件判断过多,易出错且可读性差
内容的提问来源于stack exchange,提问作者Storms786
相关产品推荐
相关产品推荐

