
视频元信息
- 视频标题:【MIT本科一年级必修】6.001 计算机程序的构造与解释(SICP)1986传奇原版·全20讲·中英双语字幕 p16 15-逻辑编程(上)
- 课程来源:MIT 6.001 Structure and Interpretation of Computer Programs(1986 年原版录像)
- 本讲主题:逻辑编程(Logic Programming)上篇——声明式知识、关系与查询语言
- 时长说明:转录文本未提供完整时长,按内容推断为单集课程的上半部分
内容摘要(TL;DR)
本讲引入逻辑编程这一全新范式:与传统的"如何做"(how-to)过程式编程相对,逻辑编程关注声明"什么是真的"(what is true)。课程以《创世记》家谱和归并排序逻辑为例,展示同一份声明式知识可回答多种不同方向的问题。随后介绍查询语言(Query Language)的三大要素——原始查询、逻辑组合(and/or/not)、规则抽象,并演示其在人员数据库上的运行。
一、从过程式思维到声明式思维 [05:07]
核心观点
课程开场重申计算机科学的本质:
"thoughts about computer programming is that it's only incidentally has to do with getting a computer to do something, primarily a computer program has to do with its way of expressing ideas, with communicating ideas." (编程只是偶然地与"让计算机做事"有关,本质上它是表达思想、交流思想的方式。)
理解:这正是 SICP 贯穿始终的哲学——程序是思想的载体。当我们想表达新类型的思想时,就需要发明新的表达模式。今天要做的就是发明一种新语言。
传统编程的局限
- 我们写的程序大多是 how-to knowledge(如何做的知识):告诉机器输入是什么、要得到什么输出。
- 例如平方根程序能回答"144 的平方根是多少?",但无法回答"17 是哪个数的平方根?"。
- 思考:这说明过程式程序带有方向性——输入与输出被严格区分,知识被"绑定"在特定的使用方向上。
逻辑编程的设想
核心目标:构建一种语言,你只需陈述事实(facts),语言自动内置通用的推理知识,能自行推导出方法。
- 你告诉它"什么是真的",它就能回答各种问题。
- 语言内置通用逻辑规则,从事实中演化出推理方法。
二、家谱例子:一份事实,多种问法 [05:52]
事实的陈述
以《创世记》家谱为例,向程序陈述事实:
- 亚当的儿子是亚伯(Abel)
- 亚当的儿子是该隐(Kane/Cain)
- 该隐的儿子是以诺(Enoch)
- 以诺的儿子是……(依此类推)
同一组事实,不同的提问
| 提问方式 | 需要的搜索程序 |
|---|---|
| 亚当的儿子是谁? | 一种搜索程序 |
| 该隐是谁的儿子? | 另一种稍有不同的搜索程序 |
| 该隐和以诺是什么关系? | 又是搜索程序的微小变体 |
关键洞察(考点):一条声明式知识(如"亚当的儿子是该隐")可以作为多种不同"如何做"知识的基础。
- 对比过程式程序:你必须明确告诉它输入和期望的输出。
- 声明式事实对"要问什么问题"是中立的(unbiased)。
理解:这就是逻辑编程威力的本质——知识与使用方式解耦。而过程式程序因为本身就是 how-to knowledge,天然倾向于寻找某个特定答案。
三、推理规则:从事实到推导 [09:21]
规则示例
在事实基础上加入推理规则(rules of inference):
如果 x 的儿子是 y,且 y 的儿子是 z,
那么 x 的孙子是 z。
(用问号表示变量,如 ?x、?y、?z)
有了这条规则,就能提问"谁的孙子是谁",或推导出所有祖孙关系。
归并(Merge)程序与底层逻辑
课程用一个具体例子——归并两个有序列表——来对比"程序"与"逻辑"。
归并程序(过程式):
- 若 x 为空,答案为 y;
- 若 y 为空,答案为 x;
- 否则比较 x 和 y 的首元素,把较小的那个接到"递归归并剩余部分"的结果上。
底层逻辑(声明式):
- 若
x 的剩余部分与y归并成z,且a小于y的首元素,则(a . x的剩余部分)与y归并成(a . z)。 - 类似地有另一条对称规则。
- 边界情形:对任意 x,
x与空列表归并成x。
重要区别(考点):
- 过程像一个"盒子":x、y 进去,答案出来——这是函数,答案由 x、y 唯一决定。
- 逻辑规则谈论的是关系(relation):
x 和 y 归并成 z,三者之间是关系,不指定哪个是输入、哪个是输出。
四、关系 vs 函数:两大本质差异 [25:29]
逻辑编程与传统编程的两大区别(核心考点)
差异一:关系而非函数
- 传统编程计算函数:有明确输入输出。
- 逻辑编程处理关系:原则上没有方向性。
- 同一份知识既能回答"1,3,7 和 2,4,8 归并成什么?",也能回答"什么和什么归并成 1,2,3,4,7,8?"。
差异二:答案不唯一
- 函数式程序对一个输入只有一个答案。
- 关系可以有多个答案。例如问"哪两个列表归并成 1,2,3,4,7,8?",理论上有多达 2⁶(64)种答案。
逻辑编程的三句箴言
逻辑编程者常说:
- 用逻辑表达什么是真的(express what is true);
- 用逻辑检验什么是真的(check whether something is true);
- 用逻辑发现什么是真的(find out what is true)。
理解:第三点"检验"对应的是是非问句(如"137 和 248 归并成 1,2,6,10 吗?"→ 否),前两点则对应开放式提问。
五、查询语言(Query Language)概览 [29:45]
与 Prolog 的关系
- 最著名的逻辑编程语言是 Prolog。
- 本课要实现的语言叫 查询语言(Query Language),本质上是 Prolog 的核心。
- 区别:查询语言在 Lisp 解释器之上实现(双重解释),因此慢得多,但能力和局限与 Prolog 基本相同。
【拓展】SICP 第 4.4 节正是"Logic Programming",用 Lisp 实现一个查询系统。这是理解 Prolog 内部机制的绝佳途径。
用三大要素分析该语言
课程再次套用贯穿全课的分析框架:
- 原始元素(primitives):有哪些基本构件?
- 组合手段(means of combination):如何把原始元素拼起来?
- 抽象手段(means of abstraction):如何命名复合构件?
六、原始元素:查询(Query)[33:20]
示例数据库
一个波士顿高科技公司的人员记录片段:
| 姓名 | 职位 | 主管 | 地址 |
|---|---|---|---|
| Ben Bitdittle | 计算机部门的 wizard | Oliver Warbucks | (略) |
| Alyssa P. Hacker | 计算机 programmer | Ben | Cambridge |
| (另一位) | programmer | Ben | (略) |
| Louis Reasoner | programmer trainee | Alyssa | (略) |
| Oliver Warbucks | 大人物(无主管) | 无 | (略) |
原始查询的语法
- 唯一的原始元素就是查询(query)。
- 语法约定:不带问号的符号是字面量(literal),带问号的(如
?x)是变量。
示例:
job ?x computer programmer
→ 匹配数据库中所有"职位是计算机程序员"的事实。
job ?x computer ?type
→ ?type 可匹配 wizard 或 programmer。
点号(dot)语法
易错点:
computer programmer匹配的是恰好两个符号的列表。而 Louis 的职位是computer programmer trainee(三个符号),不匹配。
要匹配变长列表,需用点号表示法:
job ?x computer . ?type
→ ?type 绑定为列表的剩余部分,对 Louis 来说就是 programmer trainee。
理解:这正是 Lisp 的 cons 点对表示法的直接复用,由列表读取器(list reader)自动处理。
运行示例
job ?x computer . ?y→ 找出所有计算机部门的人(4 个答案)。supervisor ?x ?y→ 列出所有主管关系。address ?x Cambridge . ?anything→ 只有一人住在 Cambridge。
输出机制:系统把查询用所有可能的变量取值实例化(instantiate)后打印出来。Prolog 则不同,它会输出
x = ...、y = ...的形式。这只是表面差异。
七、组合手段:逻辑运算 [43:05]
AND
and job ?x computer . ?y
supervisor ?x ?z
→ 找出所有计算机部门的人及其主管。
注意变量共享:两个
?x指同一个 x。x 在计算机部门工作,且 x 的主管是 z。
用 Lisp 谓词增强:lisp-value
and salary ?p ?a
lisp-value greater-than ?a 30000
→ 找出所有薪水超过 30000 的人。
lisp-value 是一个接口 hack,允许在查询中调用任意 Lisp 谓词,从而获得额外能力。但它不是逻辑,会带来代价(见下文)。
NOT
and job ?x computer . ?y
not (and supervisor ?x ?z
job ?z computer . ?w)
→ 找出所有在计算机部门工作、但没有计算机部门主管的人(即部门里的"大人物")。
组合手段小结
逻辑语言的组合手段是逻辑运算:and、or、not,外加 lisp-value 这个非逻辑的接口扩展。
八、抽象手段:规则(Rule)[48:40]
定义规则
把上面的复杂查询命名为"大人物(big shot)":
rule (big-shot ?x ?department)
(and job ?x ?department . ?y
not (and supervisor ?x ?z
job ?z ?department . ?w)))
规则的三部分
规则结构(考点):
- 表明这是规则的关键字(rule);
- 结论(conclusion);
- 主体(body)。
读法:若规则主体为真,则可推出结论为真。要推导 ?x 是某部门的大人物,验证主体即可。
归并逻辑改写为规则
将前面的归并逻辑翻译成规则:
规则 1(空列表情形,无主体):
rule (merge-to-form () ?y ?y)
无主体的规则永远为真。
规则 2(对称的空列表情形):
rule (merge-to-form ?y () ?y)
规则 3(递归情形):
rule (merge-to-form (?a . ?x) (?b . ?y) (?b . ?z))
(and (merge-to-form ?a . ?x ?y ?z)
(lisp-value greater-than ?a ?b)))
含义:若
(?a . ?x)与?y归并成?z,且 a > b,则(?a . ?x)与(?b . ?y)归并成(?b . ?z)。
规则 4(对称的递归情形):
rule (merge-to-form (?a . ?x) (?b . ?y) (?a . ?z))
(and (merge-to-form ?x (?b . ?y) ?z)
(lisp-value greater-than ?b ?a)))
运行演示
- 当函数用:
merge-to-form (1 3 7) (2 4 8) ?z→ 得到结果后系统还会继续检查是否还有其他答案(它先验地不知道只有一个答案)。 - 反向用:
merge-to-form ?x ?y (1 2 3 4)→ 找到多个解。 - 多答案用:
merge-to-form ?x ?y (1 2 3 4 5)→ 2⁵ = 32 种可能答案。
性能警告(易错点):查询语言慢的原因有三:
- 语言是双重解释的(在 Lisp 解释器之上再解释),真实实现会编译成原始操作;
- 归并算法本身是双重递归;
- 系统必须穷举所有可能性,无法先验排除。
答案以某种依赖规则尝试顺序的"任意顺序"输出。
九、答疑与关键权衡 [55:30]
提问:lisp-value 是否破坏双向查询能力?
回答:是的,会破坏(重要权衡)。
- 使用
not、lisp-value或除and以外的任何东西,都会使"何时能双向工作"变得非常复杂。- 引入
lisp-value带来更多表达能力的同时,牺牲了逻辑编程的部分原则性能力。- 这是一个必须做出的权衡(trade-off)。
理解:纯逻辑(Horn 子句 + and)有良好的理论性质,能保证较可靠的双向推理;一旦引入否定和外部谓词,就跌入非单调推理/完备性的复杂地带。
本讲小结
查询语言的三大要素:
| 要素 | 内容 |
|---|---|
| 原始元素 | 查询(query) |
| 组合手段 | and、or、not、lisp-value |
| 抽象手段 | 规则(rule) |
课程预告:下半部分将讲解该语言如何实现。
关键标签 / 关键词
SICP #逻辑编程 #LogicProgramming #查询语言 #QueryLanguage #Prolog #声明式编程 #DeclarativeProgramming #关系 #Relation #推理规则 #Rule #知识表示 #MIT6001 #Lisp #双重解释 #lisp-value #归并 #Merge #Horn子句
本讲核心记忆点回顾:
- 红色重点:关系 vs 函数的两大差异(无方向性、多答案);规则的三部分结构;
lisp-value破坏双向性的权衡。- 蓝色理解:编程是表达思想的方式;声明式知识对提问中立;纯逻辑与引入否定/外部谓词之间的理论代价。
- 黑色事实:查询语言的三大要素、示例数据库、归并规则的四种情形。
生成时间:2026-10-11 14:33:05 生成平台:VideoNote www.xygy.top