0Pricing
Scala for Backend Engineering & Functional Programming · 课时

代数数据类型

为您的领域建模

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

什么是 ADT

代数数据类型(ADT)是通过组合其他类型构建的类型。在 Scala 中,您可以使用 sealed trait,以及 case class 和 case object 变体来建模 ADT。

ADT 可以精确描述领域数据的结构。

和类型

和类型表示一个值是多个选项中的一个(OR)。交通信号灯可以是红色 OR 黄色 OR 绿色。

您可以使用一个 sealed Trait 和多个变体来表达它。

sealed trait Light
case object Red extends Light
case object Yellow extends Light
case object Green extends Light
object Main {
  def main(args: Array[String]): Unit = {
    val l: Light = Yellow
    println(l)
  }
}

积类型

积类型表示一个值由多个字段组合而成(AND)。一个点包含 x AND y。

Case class 就是积类型。

case class Point(x: Int, y: Int)
object Main {
  def main(args: Array[String]): Unit = {
    val p = Point(3, 4)
    println(s"${p.x} and ${p.y}")
  }
}

组合和类型与积类型

实际的 ADT 通常会同时使用两者:一个 sealed Trait(和类型),其变体是 case class(积类型)。

这里的 Shape 可以是 Circle(包含半径)OR Rectangle(包含宽度和高度)。

sealed trait Shape
case class Circle(radius: Double) extends Shape
case class Rectangle(w: Double, h: Double) extends Shape
object Main {
  def main(args: Array[String]): Unit = {
    val s: Shape = Rectangle(3, 4)
    println(s)
  }
}

操作 ADT

您可以通过对 ADT 的变体进行模式匹配来处理它。每个分支负责处理一种形状。

sealed trait Shape
case class Circle(radius: Double) extends Shape
case class Rectangle(w: Double, h: Double) extends Shape
object Main {
  def area(s: Shape): Double = s match {
    case Circle(r)       => 3.14159 * r * r
    case Rectangle(w, h) => w * h
  }
  def main(args: Array[String]): Unit = {
    println(area(Circle(2)))
    println(area(Rectangle(3, 4)))
  }
}

为领域建模

ADT 可以让无效状态无法表示。可以将支付方式建模为一组封闭的有效选项。

sealed trait PaymentMethod
case class CreditCard(number: String) extends PaymentMethod
case class BankTransfer(iban: String) extends PaymentMethod
case object Cash extends PaymentMethod
object Main {
  def main(args: Array[String]): Unit = {
    val m: PaymentMethod = Cash
    println(m)
  }
}

递归 ADT

ADT 可以引用自身,这正是对树和列表建模的方式。下面是一个简单的二叉树。

sealed trait Tree
case object Leaf extends Tree
case class Node(value: Int, left: Tree, right: Tree) extends Tree
object Main {
  def main(args: Array[String]): Unit = {
    val t: Tree = Node(1, Leaf, Node(2, Leaf, Leaf))
    println(t)
  }
}

折叠递归 ADT

递归加上匹配可以处理整个结构。这里我们将树中的所有值相加。

sealed trait Tree
case object Leaf extends Tree
case class Node(value: Int, left: Tree, right: Tree) extends Tree
object Main {
  def sum(t: Tree): Int = t match {
    case Leaf            => 0
    case Node(v, l, r)   => v + sum(l) + sum(r)
  }
  def main(args: Array[String]): Unit = {
    val t = Node(1, Node(2, Leaf, Leaf), Node(3, Leaf, Leaf))
    println(sum(t))
  }
}

为什么使用 ADT

ADT 功能强大,因为它们可以:

  • 通过和类型与积类型精确描述数据
  • 使非法状态无法构造
  • 与穷举模式匹配完美配合
  • 扩展到树和列表等递归结构

ADT 与继承的对比

传统的面向对象编程会将行为分散到各个子类中。ADT 保持数据定义的封闭性,并将行为放入执行模式匹配的函数中。

这种数据与行为的分离是函数式设计的典型特征。

整合运用

一个完整的小型 ADT,以及一个能够处理所有变体的函数。

sealed trait Json
case class JNum(n: Double) extends Json
case class JStr(s: String) extends Json
case object JNull extends Json
object Main {
  def render(j: Json): String = j match {
    case JNum(n) => n.toString
    case JStr(s) => "\"" + s + "\""
    case JNull   => "null"
  }
  def main(args: Array[String]): Unit = {
    List(JNum(3.0), JStr("hi"), JNull).foreach(j => println(render(j)))
  }
}

快速检查

检验您对 ADT 的理解。

回顾

您已经学会构建代数数据类型:

  • 和类型:包含多个变体的 sealed Trait(OR)
  • 积类型:包含字段的 case class(AND)
  • 将它们组合起来,精确地为领域建模
  • ADT 可以是递归的(树、列表)
  • 使用模式匹配处理它们

常见问题解答

「代数数据类型」课时是免费的吗?

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

「代数数据类型」这节课中我会学到什么?

为您的领域建模 你通过在浏览器中直接运行的动手代码来练习 Scala for Backend Engineering & Functional Programming,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

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

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

「代数数据类型」课时需要多长时间?

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

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

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

此课程中的所有课时

  1. 样例类
  2. 密封特质
  3. 代数数据类型
  4. 穷尽式匹配
← 返回 Scala for Backend Engineering & Functional Programming