面向过程与面向对象都在回答【怎么做】(命令式),函数型与逻辑型换了问题:只说【要什么】,步骤让机器自己推——这就是声明式。SQL 和正则表达式是你早就在用的声明式,只是教材没点名。
2-3 的末尾留了钩子:【状态要长期保存】这个面向对象的前提,被函数式正面质疑。本节把剩下的两块范式拼图放好,第二章的【语言分类地图】就此完整——同时回收数据库课(3-x 全章 SQL 实操)与 C++ 课(3.1.5 位运算符之外的正则应用场景)的旧账。
一、命令式 vs 声明式:两种提问方式
命令式(Imperative):一步一步下指令。【取列表 → 建空列表 → 循环 → 挑出偶数 → 逐个追加】。你指挥每一个步骤。 声明式(Declarative):描述目标本身。【给我这个列表里的所有偶数】。你只说结果长什么样,怎么遍历、怎么优化是引擎的事。
生活对照:命令式是给司机报导航(左转、直行、靠右),声明式是说目的地(去火车站)。
二、函数型:纯函数的世界
函数式编程(Functional Programming)的几条铁律:
- 纯函数:同样输入永远同样输出,不碰函数外部的任何东西(无副作用)——
f(x) = x + 1是纯函数;f()顺手改了全局变量或打了文件,就不纯; - 不可变数据:数据建好就不改,要变就造一份新的;
- 函数是一等公民:函数能当参数传、能当返回值还——【高阶函数】。
代表语言:Haskell、Elixir、F#、Clojure、Lisp 家族。但不必换语言也能尝到味道——Python 的 map/filter、Java 的 Stream API,都是函数式的移民。
三、逻辑型:事实、规则、查询
逻辑编程(Logic Programming)更极端:你只提供事实与规则,程序本身就是一个【知识库】,运行=向它提问,由推理引擎找出所有满足条件的答案。
代表语言是 Prolog:
% 事实
parent(tom, bob). % tom 是 bob 的家长
parent(bob, ann). % bob 是 ann 的家长
% 规则:祖孙关系可以推导
grandparent(X, Z) :- parent(X, Y), parent(Y, Z).
查询 grandparent(tom, ann) 引擎自动回答 true——你没有写过任何【怎么查】的步骤。它活在几个角落里:AI 教学的入门课、专家系统、约束求解。日常工程里少遇,但【描述问题,让引擎推导】这个思想随处可见(求解器、规则引擎、数据库查询计划)。
四、你早就在用声明式了
| 你熟悉的 | 为什么它是声明式 |
|---|---|
| SQL | SELECT name FROM students WHERE score > 80 —— 只描述要什么,怎么扫表走索引是数据库优化器的事(数据库课第三章整章都在写它,当时没点破这层身份) |
| 正则表达式 | 描述【长什么样】的文本,匹配算法是引擎的事 |
| HTML | 描述页面结构,浏览器负责渲染(但它是标记语言不算编程语言——见 2-5) |
| Excel 公式 | 单元格 C1 = A1+B1,你从没写过【监听 A1 变化再重算】的代码 |
五、动手 Demo:同一件事,命令式与声明式各写一遍
numbers = [12, 7, 3, 20, 15, 8, 1]
# 命令式:指挥每一步
result_imperative = []
for n in numbers:
if n % 2 == 0 and n > 10:
result_imperative.append(n * 10)
# 声明式:只说要什么
result_declarative = [n * 10 for n in numbers if n % 2 == 0 and n > 10]
# 更函数式的写法:filter + map 组合
result_functional = list(map(lambda n: n * 10,
filter(lambda n: n % 2 == 0 and n > 10, numbers)))
assert result_imperative == result_declarative == result_functional
print("三种写法结果一致:", result_declarative)
看到什么算成功:
三种写法结果一致: [120, 200]
读结果:候选里是偶数的有 12、20、8,但条件还要求大于 10——8 被筛掉,12 和 20 通过,各乘 10 得 [120, 200]。三种写法(命令式 for 循环、声明式列表推导、函数式 filter+map)被 assert 强制一致——写法是声明还是命令,答案必须一样。这个【双写对答案】的小习惯在重构时极有用:换写法前后各跑一次,不一致就说明改坏了。
六、30 秒实验
把你数据库课写过的任意一条 SQL 语句(比如 3-17 里用 AVG 求平均分那条)拿出来盯 30 秒,找出【怎么遍历表】【怎么比较】的步骤——找不到就对了,那些步骤全部发生在数据库引擎内部。你写过的每一条 SQL,都是一次【只说要什么】的声明式实践。
七、常见误区
- 【声明式就是更高级的命令式,迟早全面取代】——错。声明式把控制权交给了引擎,引擎聪明的范围内它省心,超出范围(性能调优、精确控制执行顺序)你就得回头写命令式。
- 【函数式编程=Lambda 表达式】——错。Lambda 只是把函数当值传递的语法糖;函数式的核心是纯函数与不可变。满屏 Lambda 但到处改全局变量的代码,一点函数式都不是。
- 【逻辑型语言已经死了】——不准确。Prolog 本尊小众,但它的思想活在 SQL 优化器、规则引擎、约束求解器(排课、排班、芯片验证)里——范式不死,只是搬进了库和引擎。
自测三问(点开对答案)
1. **命令式与声明式的根本区别是什么?** 答:命令式描述【怎么做】(每一步都由你指挥);声明式描述【要什么】(步骤由引擎推导)。 2. **一个函数是【纯函数】的条件是什么?** 答:同样输入永远得到同样输出,且不产生任何可观察的副作用(不改外部变量、不打 IO)。 3. **举两个你已经在用的声明式语言/工具,并说明【怎么】它们的步骤被引擎接管了。** 答:SQL(查询计划由数据库优化器生成)与正则表达式(匹配算法由引擎实现);Excel 公式亦可。- ☐ 下一节(2-5)
相关笔记
- 数据库 3-17 · SUM与AVG —— SQL 声明式身份的旧账(当时没点名)
- 计算机基础 09 · 图灵机 —— 【计算=从规则推出结果】的理论源头
- · 编程范式 —— 命令式两大范式的上半场
- ⌨️ 编程原理 · 索引 —— 全课路线图