Scala for Backend Engineering & Functional Programming · 课时

foldLeft 与 foldRight

将集合归并为一个值。

第 3 / 4 课13 个步骤

foldLeft 与 foldRight 是 CoddyKit 上的免费 Scala for Backend Engineering & Functional Programming 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Scala for Backend Engineering & Functional Programming 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。

折叠集合

折叠通过使用累加器反复组合元素,将集合归并为一个值。

您学过的累加器模式正是 fold 抽象出来的内容。您无需亲自编写递归辅助函数,只需传入初始值和组合函数。

foldLeft 基础

foldLeft 接收一个初始累加器和一个形如 (acc, element) 的函数,并从左到右遍历集合。

每一步都会使用函数的结果替换累加器。

val xs = List(1, 2, 3, 4)
val total = xs.foldLeft(0)((acc, x) => acc + x)

@main def run(): Unit =
  println(total)  // 10

foldLeft 的结合方式

foldLeft 从左侧开始结合。对于带有初始值 z 的 List(1, 2, 3),它会计算 f(f(f(z, 1), 2), 3)。

累加器是左参数,因此它会随着向列表右侧移动而不断累积。

// List(1, 2, 3).foldLeft(0)(_ + _)
// = ((0 + 1) + 2) + 3
// = 6

foldRight 基础

foldRight 同样会组合元素,但从右侧开始。

它的函数接收 (element, acc),其中元素位于左侧,累加器位于右侧。

val xs = List(1, 2, 3, 4)
val total = xs.foldRight(0)((x, acc) => x + acc)

@main def run(): Unit =
  println(total)  // 10

foldRight 的结合方式

foldRight 从右侧开始结合。对于带有初始值 z 的 List(1, 2, 3),它会计算 f(1, f(2, f(3, z)))。

初始值位于最右侧,列表从末尾开始向内组合。

// List(1, 2, 3).foldRight(0)(_ + _)
// = 1 + (2 + (3 + 0))
// = 6

方向何时重要

对于总和或乘积这类满足结合律和交换律的运算,两种折叠会得到相同结果。

对于减法或构建列表这类不满足交换律的运算,方向会改变结果。请有意识地进行选择。

val xs = List(1, 2, 3)
val l = xs.foldLeft(0)(_ - _)   // ((0-1)-2)-3 = -6
val r = xs.foldRight(0)(_ - _)  // 1-(2-(3-0)) = 2

@main def run(): Unit =
  println((l, r))  // (-6, 2)

构建列表

foldRight 是按顺序重新构建列表的自然选择,因为它从尾部向内处理,前置添加可以保持元素位置。

这种方式会映射每个元素,同时保留顺序。

val xs = List(1, 2, 3)
val doubled = xs.foldRight(List.empty[Int]) { (x, acc) =>
  (x * 2) :: acc
}

@main def run(): Unit =
  println(doubled)  // List(2, 4, 6)

foldLeft 会反转结果

如果使用 foldLeft 并通过前置添加来构建列表,结果会被反转,因为元素会随着向右移动而从前端依次添加。

有时这正是您想要的结果。

val xs = List(1, 2, 3)
val rev = xs.foldLeft(List.empty[Int]) { (acc, x) =>
  x :: acc
}

@main def run(): Unit =
  println(rev)  // List(3, 2, 1)

栈安全

foldLeft 是尾递归的,并且会作为循环运行,因此可以安全处理超大集合。

List 上的 foldRight 不是尾递归的,处理很长的列表时可能导致栈溢出。如果不需要从右到左的顺序,优先使用 foldLeft。

// Safe even for millions of elements:
val n = (1 to 1000000).foldLeft(0L)(_ + _)

// foldRight on a long List risks StackOverflowError

改变结果类型

累加器类型可以不同于元素类型。

这里我们将一个整数列表折叠为字符串,因此初始值是空字符串,每一步都会执行追加操作。

折叠的类型由初始值决定。

val xs = List(1, 2, 3)
val s = xs.foldLeft("")((acc, x) => acc + x.toString)

@main def run(): Unit =
  println(s)  // "123"

折叠:一把瑞士军刀

许多列表操作都是 fold 的特例:sum、product、length、max、map、filter、reverse。

认识到这些操作背后的 fold,有助于您编写简洁的声明式代码,而不是手动编写递归。

val xs = List(4, 1, 7, 3)
val maxV = xs.foldLeft(Int.MinValue)(_ max _)
val len  = xs.foldLeft(0)((acc, _) => acc + 1)

@main def run(): Unit =
  println((maxV, len))  // (7, 4)

快速检查

思考折叠方向和初始值的位置。

回顾

foldLeft 从左到右遍历,累加器位于左侧,并计算 ((z op a) op b) op c。它是尾递归的,并且具有栈安全性。

foldRight 从右到左遍历,初始值位于右侧,并计算 a op (b op (c op z))。它适合按顺序构建列表,但处理很长的列表时可能导致栈溢出。

初始值决定结果类型,因此折叠可以将集合转换为任意值。

免费开始

用 AI 导师学习 Scala — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
39
课程
143

常见问题解答

「foldLeft 与 foldRight」课时是免费的吗?

是的 — 「foldLeft 与 foldRight」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Scala for Backend Engineering & Functional Programming 课程的其余内容,请升级到 CoddyKit PRO。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。

「foldLeft 与 foldRight」这节课中我会学到什么?

将集合归并为一个值。 你通过在浏览器中直接运行的动手代码来练习 Scala for Backend Engineering & Functional Programming,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Scala for Backend Engineering & Functional Programming 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Scala for Backend Engineering & Functional Programming 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「foldLeft 与 foldRight」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Scala for Backend Engineering & Functional Programming 课中编写并运行代码吗?

能。每节 Scala for Backend Engineering & Functional Programming 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 递归思维
  2. 累加器模式
  3. foldLeft 与 foldRight
  4. reduce 与聚合
← 返回 Scala for Backend Engineering & Functional Programming