# 声明式编程概念

> CIE A-Level 计算机科学 · 9618 (2022-2024)
> 来源: https://www.owlsprep.com/zh/study/cie-9618-u11-declarative-programming-concepts/

本模块讲解CIE 9618试卷1的核心声明式编程概念。你将学习比较声明式和命令式范型，掌握核心特征，并了解考试常考的常见子范型。

**先修:** [编程基础](https://www.owlsprep.com/zh/study/cie-9618-u10-programming-fundamentals/)

## 学习目标

- 区分声明式和命令式编程范型
- 识别声明式语言的核心特征
- 解释声明式编程的两大主要子范型
- 认识考试中关于声明式概念的常见题型

## 核心定义：声明式编程与命令式编程

**声明式编程** — 一种专注于描述*需要解决什么问题*，而非显式编码*如何一步步解决问题*的编程范型。

*例:* 一条返回所有分数超过70分学生的SQL查询就是声明式的。

**例题:** 比较对数字列表排序任务的声明式描述和命令式描述

1. 命令式方法会显式定义解决方案的每一步：
`从索引0迭代到n-1，比较相邻元素，如果顺序不对就交换，重复直到没有交换。`
2. 声明式方法只描述期望的最终状态，没有任何实现步骤：
`返回一个包含输入所有元素、按升序排序的新列表。`

> **tip**
>
> 考试几乎总是考察做什么/怎么做的区别。做比较题时要时刻记住这一点。

## 声明式编程的核心特征

声明式编程有四个核心特征，是CIE考试中经常考察的内容：

- **无可变副作用**：大多数声明式语言避免修改共享状态或可变数据，减少错误。
- **高度抽象**：实现细节对程序员隐藏，程序员只需要定义需求。
- **引用透明性**：对于相同输入，函数总是返回相同输出，无论在何处调用。
- **无显式控制流**：循环、条件判断和搜索由语言解释器处理，不需要程序员编写。

**例题:** 判断下列哪段代码片段是声明式的，并解释原因：

1. Snippet 1: `SELECT name FROM students WHERE grade > 70;`
2. Snippet 2: `result = []; for s in students: if s.grade > 70: result.append(s.name)`
3. 代码片段1（SQL）是声明式的：它只描述我们想要什么结果，不描述如何扫描表或筛选行，这些步骤由SQL引擎处理。
4. 代码片段2是命令式的：它明确定义了循环、条件和构建结果的操作，详细说明了过程的每一步。

**概念自测**

测试你对核心特征的理解

1. 下列哪一项是声明式编程的核心特征？

   - A) 显式逐步控制流
   - B) 描述需要什么结果
   - C) 可变共享状态
   - D) 底层内存管理

   *解析:* 正确。所有其他选项都是命令式编程的核心特征，而非声明式。

## 声明式编程的子范型

声明式编程是一个涵盖术语，包含CIE 9618大纲明确覆盖的两个主要子范型：

| 子范型 | 核心思想 | 示例语言 |
| --- | --- | --- |
| 逻辑编程 | 程序是事实和规则的集合，由解释器求解查询 | Prolog |
| 函数式编程 | 程序由纯函数构建，无可变状态 | Haskell, Scala, Python (支持函数式特性) |

**例题:** 解释Prolog中祖先关系的代码如何符合声明式范型

1. 首先，我们定义关于亲子关系的事实：
2. `parent(john, mary). 
parent(mary, ann).`
3. 接下来，我们定义祖先关系的规则：
4. `ancestor(X, Y) :- parent(X, Y). 
ancestor(X, Y) :- parent(X, Z), ancestor(Z, Y).`
5. 这段代码只描述了什么是真，不描述如何求解`ancestor(john, ann)`这样的查询。Prolog解释器会处理搜索和匹配步骤，因此符合声明式'描述做什么而非怎么做'的规则。

**考试命令词**

- **Distinguish between declarative and imperative** — 你需要说明至少一个清晰的区别，通常是做什么/怎么做的区别，如果题目要求则需要给出例子 *(一定要提到声明式描述所需结果，命令式描述得到结果的步骤。)*

## 常见错误

- **错误做法:** 声称所有声明式语言都是函数式的，并且所有函数式代码都是纯声明式的。
  - 原因: 声明式是一个宽泛的范型类别，函数式只是其中一个子范型，而且许多函数式语言允许编写命令式代码。
  - 正确做法: 应当说明函数式编程是声明式编程的一个子范型，逻辑编程是另一个独立且同等重要的子范型。
- **错误做法:** 将面向对象编程（OOP）与声明式编程混淆。
  - 原因: OOP是一种命令式范型，它围绕可变对象和逐步方法组织代码。
  - 正确做法: 记住OOP属于命令式范型的范畴，因为它专注于如何修改对象状态来得到结果。
- **错误做法:** 声称SQL不是声明式的，因为它有过程化扩展。
  - 原因: CIE将核心SQL查询归类为声明式，过程化扩展是可选附加组件，不用于标准查询。
  - 正确做法: 确认标准SQL查询是声明式的，因为它们描述所需结果，而非如何检索结果。
- **错误做法:** 认为声明式代码总是比命令式代码效率更高。
  - 原因: 效率取决于使用场景，隐藏底层实现会让声明式代码在某些任务中更慢。
  - 正确做法: 认识到声明式代码的主要优势是降低复杂度和更容易调试，不保证性能更好。

## 速查表

| 特征 | 声明式编程 | 命令式编程 |
| --- | --- | --- |
| 核心关注点 | 需要什么结果 | 如何生成结果 |
| 控制流 | 由语言处理 | 由程序员显式定义 |
| 引用透明性 | 通常支持 | 不需要 |

---

来自 [OwlsPrep](https://www.owlsprep.com) —— A-Level / IB / AP / IGCSE 免费学习指南，依据官方考纲编写。原页面：https://www.owlsprep.com/zh/study/cie-9618-u11-declarative-programming-concepts/
