代码拉取完成,页面将自动刷新
1
00:00:00,000 --> 00:00:17,814
【背景音乐:巴赫】
2
00:00:17,814 --> 00:00:22,132
教授:上次课我们学习了如何构建一门语言。
3
00:00:22,132 --> 00:00:26,050
要点是解释器(比如lisp解释器)
4
00:00:26,050 --> 00:00:27,580
是由两个主要部分构成的。
5
00:00:27,580 --> 00:00:36,350
一个是EVAL。EVAL负责接收一个表达式(expression)和环境(environment)
6
00:00:36,350 --> 00:00:43,820
并将其转换成一个过程和一些参数,
7
00:00:43,820 --> 00:00:46,635
然后传递给APPLY。
8
00:00:49,410 --> 00:00:52,250
APP接收该过程和参数,再将其
9
00:00:52,250 --> 00:00:55,680
转换为(一般情况下)另一个表达式。
10
00:00:55,680 --> 00:00:58,280
该表达式将结合另外一个环境求值。APPLY将表达式回传给EVAL,
11
00:00:58,280 --> 00:01:00,770
EVAL再传给APPLY,
12
00:01:00,770 --> 00:01:02,750
形成一个大的循环,
13
00:01:02,750 --> 00:01:05,519
直至被翻译成基本数据
14
00:01:05,519 --> 00:01:07,740
或基本过程
15
00:01:07,740 --> 00:01:12,080
这个循环所作的工作就是分解
16
00:01:12,080 --> 00:01:15,020
语言当中的组合和抽象。
17
00:01:15,020 --> 00:01:17,870
比如,你有一个LISP过程 --
18
00:01:17,870 --> 00:01:21,320
一个通用的,可以用来
19
00:01:21,320 --> 00:01:25,392
对这个表达式代入任何参数进行求值,
20
00:01:25,392 --> 00:01:27,670
和我们正在做的差不多。
21
00:01:27,670 --> 00:01:28,510
这就是APPLY的工作。
22
00:01:28,510 --> 00:01:30,770
它规定将所有作为参数传进来的值
23
00:01:30,770 --> 00:01:33,380
归约为表达式的主体。
24
00:01:33,380 --> 00:01:35,790
如果这是一个复合表达式,或调用了
25
00:01:35,790 --> 00:01:40,440
另外一个过程,就会一直循环下去。
26
00:01:40,440 --> 00:01:43,040
哇,这就是几乎所有
27
00:01:43,040 --> 00:01:45,120
解释器的基本结构了。
28
00:01:45,120 --> 00:01:46,720
另外就是,当你拿到一个解释器后,
29
00:01:46,720 --> 00:01:49,080
你就可以随心所欲的摆布
30
00:01:49,080 --> 00:01:49,870
你的语言了。
31
00:01:49,870 --> 00:01:53,390
你可以加入对动态范围的支持,
32
00:01:53,390 --> 00:01:55,960
正常次序求值,或者为语言增加一种
33
00:01:55,960 --> 00:01:57,680
新的变形,随便怎样都行。
34
00:01:57,680 --> 00:02:00,570
更一般的,我们遇到了元语言抽象
35
00:02:00,570 --> 00:02:07,930
就是说你作为一个工程师,一个软件工程师,
36
00:02:07,930 --> 00:02:09,970
而不仅是一个一般的工程师,经常可以通过发明
37
00:02:09,970 --> 00:02:15,270
新的语言以对复杂度
38
00:02:15,270 --> 00:02:18,010
进行控制。
39
00:02:18,010 --> 00:02:22,830
从某个角度上看,计算机编程
40
00:02:22,830 --> 00:02:25,170
仅仅是偶然的,刚好利用到了
41
00:02:25,170 --> 00:02:26,440
计算机来做事情。
42
00:02:26,440 --> 00:02:29,220
计算机程序主要是一种
43
00:02:29,220 --> 00:02:33,270
表达,交流想法的方法。
44
00:02:33,270 --> 00:02:36,300
有时,为了表达新的想法,
45
00:02:36,300 --> 00:02:39,770
你会更喜欢发明一种新的模式。
46
00:02:39,770 --> 00:02:44,300
好,今天我们将使用这个框架来
47
00:02:44,300 --> 00:02:45,730
构建一门新语言。
48
00:02:45,730 --> 00:02:48,140
一旦我们掌握了解释器的基本原理,
49
00:02:48,140 --> 00:02:50,830
你就可以构建任何你喜欢的语言。
50
00:02:50,830 --> 00:02:54,370
比如我们可以构建Pascal。
51
00:02:54,370 --> 00:02:58,820
确实有很多需要考虑的东西:语法,解析,
52
00:02:58,820 --> 00:03:01,450
各种各样的编译器优化,而且有很多人
53
00:03:01,450 --> 00:03:05,580
以此为生,过着诚实的生活。
54
00:03:05,580 --> 00:03:09,100
但在我们讨论的抽象层级上,一个Pascal解释器
55
00:03:09,100 --> 00:03:13,020
和上次Gerry做的那个,
56
00:03:13,020 --> 00:03:15,350
没有任何区别。
57
00:03:15,350 --> 00:03:18,190
今天我们不做那个。我们要构建一门
58
00:03:18,190 --> 00:03:23,400
真正与众不同的语言。
59
00:03:23,400 --> 00:03:26,980
它可以引导你跳出过程(procedure),
60
00:03:26,980 --> 00:03:29,090
以一种完全不同的方式去看待程序设计。
61
00:03:29,090 --> 00:03:33,650
今天的课程会同时在两个层面上展开。
62
00:03:34,810 --> 00:03:37,210
一方面,我会介绍这门语言长什么样,
63
00:03:37,210 --> 00:03:40,410
另一方面,我会向你们展示如何实现它。
64
00:03:41,010 --> 00:03:43,250
我们会用LISP去实现它,
65
00:03:43,250 --> 00:03:44,220
并研究它如何工作。
66
00:03:44,220 --> 00:03:48,730
你们将从两个层面上学习。
67
00:03:48,730 --> 00:03:52,190
一个是要认识到语言可以如此的与众不同,
68
00:03:53,790 --> 00:03:57,830
以至于如果你们认为从Fortran到LISP
69
00:03:57,830 --> 00:04:01,560
是一个巨大的跨越,那实际上你们还没开眼。
70
00:04:01,560 --> 00:04:05,660
第二,你们会看到,即使是
71
00:04:05,660 --> 00:04:08,590
这样一门特立独行的语言,
72
00:04:08,590 --> 00:04:12,260
它完全没有过程,也没有函数,
73
00:04:12,260 --> 00:04:16,570
但它背后还是基本的求值(eval)、应用(apply)循环,
74
00:04:16,570 --> 00:04:19,170
负责分解组合以及抽象的各种方法。
75
00:04:20,950 --> 00:04:24,430
第三点,作为一个很小但很优雅的技术点,
76
00:04:24,430 --> 00:04:27,720
你们会看到如何使用流(Stream)避免回溯(backtracking)。
77
00:04:32,330 --> 00:04:35,860
好了,我说过这门语言非常特别。
78
00:04:35,860 --> 00:04:41,620
为了解释它为什么特别,让我们回到在课程之初
79
00:04:41,620 --> 00:04:44,710
提到的第一个观点,
80
00:04:44,710 --> 00:04:48,780
关于声明式知识和数学之间的差异。
81
00:04:50,240 --> 00:04:55,470
对平方根的定义是一个数学真理--
82
00:04:55,470 --> 00:04:59,080
而计算机科学是关于“怎么做”的知识--
83
00:04:59,810 --> 00:05:03,700
对比一下平方根的定义
84
00:05:03,700 --> 00:05:05,970
和一个用来计算平方根的程序。
85
00:05:05,970 --> 00:05:08,042
我们从这里开始。
86
00:05:08,042 --> 00:05:11,830
如果我们能做到这样,是不是很伟大:
87
00:05:11,830 --> 00:05:16,030
发明一种新的语言填平这道沟。它可以执行计算,
88
00:05:16,030 --> 00:05:20,510
但我们用声明式的,用描述事实的方法去和它交流。
89
00:05:22,380 --> 00:05:24,110
在这样的语言里
90
00:05:24,110 --> 00:05:27,690
你们定义事实
91
00:05:27,690 --> 00:05:28,880
你们告诉它“是什么”
92
00:05:28,880 --> 00:05:30,950
你们告诉它“什么是真”
93
00:05:30,950 --> 00:05:34,220
然后当你们需要答案时,
94
00:05:34,220 --> 00:05:38,560
植语言已经入了一般性的
95
00:05:38,560 --> 00:05:41,200
关于“怎么做”的知识。它就可以接受你们输入的事实
96
00:05:41,200 --> 00:05:44,180
并基于这些事实,以及一些通用的逻辑规则,
97
00:05:44,180 --> 00:05:46,200
一步步去行演算。
98
00:05:49,330 --> 00:05:53,920
举个例子,我可以对这个程序说:
99
00:05:55,645 --> 00:06:08,920
我告诉它Adam的儿子是Abel。
100
00:06:08,920 --> 00:06:17,660
Adam的儿子是Cain。
101
00:06:17,660 --> 00:06:24,670
Cain的儿子是Enoch。
102
00:06:27,502 --> 00:06:37,550
Enoch的儿子是Irad,
103
00:06:37,550 --> 00:06:41,190
以及创世纪中提到的所有信息,
104
00:06:41,190 --> 00:06:45,010
一直到Adah结束。
105
00:06:45,010 --> 00:06:48,760
这就是从Cain到Adah的完整谱系。
106
00:06:48,760 --> 00:06:52,520
总之,一旦你告诉了它这些事实
107
00:06:52,520 --> 00:06:53,510
你就可以向它提问。
108
00:06:53,510 --> 00:06:58,560
你可以向这门语言提问:
109
00:06:58,560 --> 00:07:00,420
谁是Adam的儿子?
110
00:07:00,420 --> 00:07:03,480
很容易你就能想到
111
00:07:03,480 --> 00:07:06,460
用一个通用的搜索程序
112
00:07:06,460 --> 00:07:08,800
进行搜索并回答,yeah,有两个答案
113
00:07:08,800 --> 00:07:10,930
Adam的儿子是Abel
114
00:07:10,930 --> 00:07:14,140
Adam的儿子是Cain。
115
00:07:14,140 --> 00:07:19,350
或者你可以问,基于同样的事实,
116
00:07:19,350 --> 00:07:21,950
Cain是谁的儿子?
117
00:07:21,950 --> 00:07:25,520
然后你又可以想象一下产生一段
118
00:07:25,520 --> 00:07:29,510
稍微有些不同的搜索程序,可以查询事实
119
00:07:29,510 --> 00:07:33,760
并发现谁是Cain,他是谁的儿子,
120
00:07:33,760 --> 00:07:35,890
然后找到Adam。
121
00:07:35,890 --> 00:07:40,300
或者你可以问
122
00:07:40,300 --> 00:07:42,070
Cain和Enoch是什么关系?
123
00:07:42,070 --> 00:07:46,340
再一次的,该搜索程序又有一个小变种
124
00:07:46,340 --> 00:07:48,160
你能得到他们是父子关系。
125
00:07:52,880 --> 00:07:56,960
即使在这个简单的例子里
126
00:07:56,960 --> 00:08:00,460
你们可以发现,同一个简单的事实,比如
127
00:08:00,460 --> 00:08:04,230
Adam的儿子是Cain,可以被用来
128
00:08:04,230 --> 00:08:06,520
回答很多个不同的问题。
129
00:08:06,520 --> 00:08:10,540
你可以问,谁是Adam的儿子
130
00:08:10,540 --> 00:08:12,220
或者你可以问亚当和该隐是什么关系
131
00:08:12,970 --> 00:08:17,370
这些问题由依据同样的事实的
132
00:08:17,370 --> 00:08:22,474
一些不同的传统过程所回答。
133
00:08:22,474 --> 00:08:24,960
这就是这种编程风格的精华所在
134
00:08:24,960 --> 00:08:30,050
一条声明式的知识
135
00:08:30,050 --> 00:08:33,150
可以被用做许多不同种类的”怎么做“
136
00:08:33,150 --> 00:08:36,440
的问题的基础,而不是我们以前写的那种过程
137
00:08:36,440 --> 00:08:39,010
告诉它输入是什么
138
00:08:39,010 --> 00:08:41,490
期望什么样的答案。
139
00:08:41,490 --> 00:08:43,710
比如,我们的平方根程序可以完美的
140
00:08:43,710 --> 00:08:48,900
回答这样的问题,144的平方根是多少?
141
00:08:48,900 --> 00:08:51,290
但本质上,平方根的数学定义
142
00:08:51,290 --> 00:08:52,830
可以回答更多的问题
143
00:08:52,830 --> 00:08:57,590
比如17是哪个数的平方根?
144
00:08:57,590 --> 00:08:58,590
这个问题可能需要使用一个
145
00:08:58,590 --> 00:09:01,920
不同的程序来回答。
146
00:09:01,920 --> 00:09:05,700
所以数学定义,或者更一般的,
147
00:09:05,700 --> 00:09:09,540
我们告诉语言的事实,
148
00:09:09,540 --> 00:09:10,900
跟问题没有强绑定关系。
149
00:09:10,900 --> 00:09:13,240
但是我们习惯与特定的程序,因为
150
00:09:13,240 --> 00:09:15,230
它们是关于”怎么做“的知识,
151
00:09:15,230 --> 00:09:17,700
查找某个特定的答案。
152
00:09:17,700 --> 00:09:19,530
所以这将是我们将要讨论的一个特征。
153
00:09:21,810 --> 00:09:23,480
我们继续。
154
00:09:23,480 --> 00:09:26,420
设想我们已经给我们的语言
155
00:09:26,420 --> 00:09:27,710
输入了一些事实。
156
00:09:27,710 --> 00:09:30,020
现在让我们再给它一些推导规则。
157
00:09:30,020 --> 00:09:35,100
举个例子,我们可以说,如果---
158
00:09:35,100 --> 00:09:36,510
这里编一些语法---
159
00:09:36,510 --> 00:09:41,580
如果x的儿子是y--
160
00:09:41,580 --> 00:09:45,650
我用问号表示这是一个变量--
161
00:09:45,650 --> 00:10:01,800
如果x的儿子是y,且y的儿子是z,
162
00:10:01,800 --> 00:10:09,320
那么x的孙子是z。
163
00:10:09,320 --> 00:10:15,370
所以我可以设想一下,把规则告诉机器
164
00:10:15,370 --> 00:10:17,680
然后问它,比如说,
165
00:10:17,680 --> 00:10:20,610
谁是亚当的孙子?
166
00:10:20,610 --> 00:10:24,790
或者Irad是谁的孙子?
167
00:10:24,790 --> 00:10:28,080
或者基于已知信息,
168
00:10:28,080 --> 00:10:29,330
找出所有的祖孙关系。
169
00:10:31,220 --> 00:10:34,580
我们不妨设想一下,语言知道如何
170
00:10:34,580 --> 00:10:35,830
自动的回答这些问题。
171
00:10:42,640 --> 00:10:45,200
让我在举几个更具体的例子。
172
00:10:49,610 --> 00:10:53,700
这是一个用来合并两个有序列表的过程。
173
00:10:53,700 --> 00:11:01,370
x和y是两个数字的列表,每个列表中没有重复元素。
174
00:11:01,370 --> 00:11:04,780
然后,如果你们愿意的话,按升序排列。
175
00:11:04,780 --> 00:11:08,560
merge的作用是取两个列表,
176
00:11:08,560 --> 00:11:10,040
合并成一个列表,仍然按升序排列。
177
00:11:10,040 --> 00:11:15,330
这么简单的程序,你们应该
178
00:11:15,330 --> 00:11:16,390
都会写。
179
00:11:16,390 --> 00:11:18,860
如果x为空,结果为y。
180
00:11:18,860 --> 00:11:21,180
如果y为空,结果为x。
181
00:11:21,180 --> 00:11:22,990
否则,比较各自的第一个元素。
182
00:11:22,990 --> 00:11:25,540
取出x的第一个元素和y中的第一个元素
183
00:11:25,540 --> 00:11:31,060
哪个小,就把哪个元素放到一边,然后对剩余部分
184
00:11:31,060 --> 00:11:35,500
(或者把x的头去掉,或者把y的头去掉)
185
00:11:35,500 --> 00:11:40,150
再和刚才取出来的元素合并。
186
00:11:42,400 --> 00:11:43,960
这是一个标准的程序。
187
00:11:46,470 --> 00:11:48,620
我们看一下它的逻辑。
188
00:11:48,620 --> 00:11:51,660
忘掉刚才的程序。看看这个过程所
189
00:11:51,660 --> 00:11:53,820
基于的逻辑。
190
00:11:53,820 --> 00:11:56,860
看,这里逻辑是,如果第一个
191
00:11:56,860 --> 00:12:00,240
更小一些,我们就把某个东西和
192
00:12:00,240 --> 00:12:03,350
剩余的部分递归合并的结果进行合并。
193
00:12:03,350 --> 00:12:05,420
我们试一下准确的说出使程序工作的逻辑。
194
00:12:08,430 --> 00:12:10,130
这一块,
195
00:12:10,130 --> 00:12:13,820
这块程序递归的
196
00:12:13,820 --> 00:12:19,980
当x更小的时候消去x的首元素。
197
00:12:19,980 --> 00:12:22,030
如果我们想要更准确的给出这里的逻辑
198
00:12:22,030 --> 00:12:27,120
那么这实际上是一个推导过程,
199
00:12:27,120 --> 00:12:31,790
即,如果我们知道cdr x和y合并等于z,
200
00:12:40,480 --> 00:12:47,570
且a比y的首元素小,我们就知道
201
00:12:47,570 --> 00:12:55,820
如果把a合并到cdr x的之前,再与y合并,结果就等于a+z。
202
00:12:55,820 --> 00:12:58,720
这就是背后的逻辑。
203
00:12:58,720 --> 00:13:01,620
我没有把它写成程序,而是写成一种推导。
204
00:13:01,620 --> 00:13:05,480
它是这段话背后的东西。
205
00:13:05,480 --> 00:13:09,410
它决定了在这里可以使用递归。
206
00:13:09,410 --> 00:13:11,910
类似地,看另外一段,
207
00:13:11,910 --> 00:13:14,000
把它做完。
208
00:13:14,000 --> 00:13:16,880
另外一段基于几乎相同的逻辑
209
00:13:16,880 --> 00:13:19,460
我就不细说了。
210
00:13:19,460 --> 00:13:22,730
然后这是我们要测试的n个场景。它基于这样的思想:
211
00:13:22,730 --> 00:13:26,920
任何x与空list合并还是x
212
00:13:26,920 --> 00:13:30,740
任何y与空list合并还是y
213
00:13:33,360 --> 00:13:39,340
好。我们看到了一段过程,以及它所基于的逻辑。
214
00:13:41,740 --> 00:13:44,750
注意一个巨大的差异
215
00:13:44,750 --> 00:13:51,050
过程是这样的。
216
00:13:51,050 --> 00:13:52,900
它规定这里有一个盒子。
217
00:13:52,900 --> 00:13:55,410
我们做的所有的事情都有这样的特点
218
00:13:55,410 --> 00:13:57,890
一个盒子,有东西进来,有东西出去。
219
00:13:57,890 --> 00:14:04,480
这个盒子叫merge,进来了x和y
220
00:14:04,480 --> 00:14:07,550
出来一个答案
221
00:14:07,550 --> 00:14:09,340
这就是过程的特点。
222
00:14:13,160 --> 00:14:14,660
规则就不一样
223
00:14:14,660 --> 00:14:17,620
规则讲的是关系
224
00:14:17,620 --> 00:14:23,030
这些胶片里有一些我称为
225
00:14:23,030 --> 00:14:25,370
"合并"的规则
226
00:14:25,370 --> 00:14:29,200
我说x和y合并等于z
227
00:14:29,200 --> 00:14:32,610
这还是个函数
228
00:14:32,610 --> 00:14:32,850
对吧?
229
00:14:32,850 --> 00:14:36,070
答案是x和y的函数。这里我看到的
230
00:14:36,070 --> 00:14:39,720
是三个东西之间的关系
231
00:14:39,720 --> 00:14:43,120
我不会规定哪些是输入
232
00:14:43,120 --> 00:14:44,200
那些是输出
233
00:14:44,200 --> 00:14:48,690
我这样说的原因是因为,原则上
234
00:14:48,690 --> 00:14:51,300
我们可以用这些同样的逻辑规则去回答
235
00:14:51,300 --> 00:14:54,570
很多个不同的问题
236
00:14:54,570 --> 00:14:56,750
所以我们可以,比如,
237
00:14:56,750 --> 00:14:59,050
假设把这些逻辑规则交给机器。
238
00:14:59,050 --> 00:15:01,400
不是程序,而是背后的逻辑规则
239
00:15:01,400 --> 00:15:04,750
于是它就可以回答,
240
00:15:04,750 --> 00:15:06,770
比如我们可以问--
241
00:15:06,770 --> 00:15:20,910
1, 3, 7和2, 4, 8合并等于什么?
242
00:15:20,910 --> 00:15:23,880
这个问题它应该可以回答
243
00:15:23,880 --> 00:15:26,480
这恰恰是我们的lisp
244
00:15:26,480 --> 00:15:28,180
过程所回答的问题
245
00:15:28,180 --> 00:15:33,750
它同样的规则还可以回答
246
00:15:33,750 --> 00:15:41,760
这样的问题:1, 3, 7和什么合并可以得到
247
00:15:41,760 --> 00:15:45,560
1, 2, 3, 4, 7, 8?
248
00:15:45,560 --> 00:15:48,120
同样的一组规则可以回答它,但是
249
00:15:48,120 --> 00:15:50,880
刚才写的过程就不行
250
00:15:50,880 --> 00:15:56,070
或者我们可以说
251
00:15:56,070 --> 00:16:07,900
什么与什么合并得到----
252
00:16:07,900 --> 00:16:13,780
什么与什么合并得到1,2,3,4,7,8?
253
00:16:13,780 --> 00:16:16,320
如果这个东西真的可以应用刚才的逻辑,它可以一直跑下去
254
00:16:16,320 --> 00:16:20,470
推导出第2~6个问题的答案
255
00:16:25,600 --> 00:16:28,790
它可以是1和其它,或者1,2与其它
256
00:16:28,790 --> 00:16:32,490
或者1,3,7与其它。
257
00:16:32,490 --> 00:16:33,410
有很多个答案。
258
00:16:33,410 --> 00:16:36,830
原则上,这个逻辑
259
00:16:36,830 --> 00:16:38,550
足以推导出它们。
260
00:16:38,550 --> 00:16:44,540
所以,在我们将要研究的程序
261
00:16:44,540 --> 00:16:48,370
和其它程序
262
00:16:48,370 --> 00:16:49,850
包括lisp和差不多你们之前见过的所有程序之间
263
00:16:49,850 --> 00:16:54,150
存在着两个巨大的差异
264
00:16:54,150 --> 00:16:57,620
首先,我们不会去计算函数。
265
00:17:00,800 --> 00:17:03,770
我们不会去关注接受输入返回输出的东西。
266
00:17:04,410 --> 00:17:06,890
我们将讨论关系。
267
00:17:06,890 --> 00:17:09,180
原则上,这些关系
268
00:17:09,180 --> 00:17:11,089
是没有方向的。
269
00:17:11,089 --> 00:17:14,569
所以你们所定义的,用来回答这个问题的知识
270
00:17:14,569 --> 00:17:19,220
可以同样的用来回答这些问题
271
00:17:19,220 --> 00:17:21,345
以及反过来的问题。
272
00:17:26,310 --> 00:17:30,590
第二个问题是,因为我们讨论的是关系
273
00:17:30,590 --> 00:17:33,150
那么这些关系就未必
274
00:17:33,150 --> 00:17:35,610
只有一个答案
275
00:17:35,610 --> 00:17:37,480
所以底下的第三个问题
276
00:17:37,480 --> 00:17:39,415
没有一个唯一的答案,它有一堆答案。
277
00:17:42,270 --> 00:17:44,640
OK, 这就是我们下面要学习的。
278
00:17:44,640 --> 00:17:48,620
另外,这种编程风格,
279
00:17:48,620 --> 00:17:51,310
称为逻辑编程。原因很明显。
280
00:17:56,160 --> 00:18:02,440
使用逻辑编程的人这么说 ---
281
00:18:02,440 --> 00:18:04,150
---逻辑编程的关键就在于:
282
00:18:04,150 --> 00:18:10,190
用逻辑表达什么是真
283
00:18:10,190 --> 00:18:15,190
用逻辑去检查什么为真
284
00:18:15,190 --> 00:18:19,200
用逻辑去寻找什么为真。
285
00:18:19,200 --> 00:18:23,300
已知的最好的逻辑编程语言
286
00:18:23,300 --> 00:18:25,780
你们可能听说过,是Prolog.
287
00:18:25,780 --> 00:18:31,010
今天上午我们将要实现的语言,
288
00:18:31,010 --> 00:18:33,110
叫做query语言
289
00:18:33,110 --> 00:18:35,320
它具有Prolog的精华。
290
00:18:35,320 --> 00:18:38,340
它能做同样的事。当然它很慢
291
00:18:38,340 --> 00:18:42,390
因为我们将要用LISP去实现它
292
00:18:42,390 --> 00:18:44,210
而不是去开发一个专用的编译器。
293
00:18:44,210 --> 00:18:47,510
我们将要在LISP解释器的基础上实现新语言的解释器。
294
00:18:47,510 --> 00:18:48,950
除此以外,新语言的功能
295
00:18:48,950 --> 00:18:49,750
和prolog是一样的。
296
00:18:49,750 --> 00:18:52,160
它的能力和限制
297
00:18:52,160 --> 00:18:54,696
几乎都一样。
298
00:18:54,696 --> 00:18:56,120
我们暂停一下,问题时间
299
00:19:00,040 --> 00:19:04,010
学生:能否请您重复一下
300
00:19:04,010 --> 00:19:06,720
使用逻辑编程的三个目的?
301
00:19:06,720 --> 00:19:09,120
换句话说,是不是“寻找什么是真”,
302
00:19:09,120 --> 00:19:09,840
“学习什么是真”,“什么是真”?
303
00:19:09,840 --> 00:19:10,520
教授:是的。
304
00:19:10,520 --> 00:19:15,850
可以说是逻辑程序员的教义吧。
305
00:19:15,850 --> 00:19:22,610
你用逻辑来表达什么是真,就像这些规则一样。
306
00:19:22,610 --> 00:19:26,120
你使用逻辑来检查某些东西是否为真。这是一些我还没有回答过的问题。
307
00:19:28,550 --> 00:19:29,720
我可以说 --
308
00:19:29,720 --> 00:19:33,620
我来写另一个问题,
309
00:19:33,620 --> 00:19:41,400
1,3,7和2,4,8是否合并成1,2,6,10?
310
00:19:41,400 --> 00:19:45,690
同样的逻辑足以判断出这是假的。
311
00:19:45,690 --> 00:19:49,190
所以我使用逻辑来判断什么是真,
312
00:19:49,190 --> 00:19:50,480
你还可以用逻辑查找什么是真。
313
00:20:04,060 --> 00:20:04,570
好。
314
00:20:04,570 --> 00:20:06,138
我们休息。
315
00:20:06,138 --> 00:20:22,106
【巴赫的音乐】
316
00:20:22,106 --> 00:20:47,590
音乐结束
317
00:20:47,590 --> 00:21:02,901
【巴赫的音乐】
318
00:21:02,901 --> 00:21:06,810
教授:继续。
319
00:21:06,810 --> 00:21:10,520
看一下这个查询语言和操作。
320
00:21:10,520 --> 00:21:12,890
当你看到这个小圣经数据库时,
321
00:21:12,890 --> 00:21:15,390
你注意到的第一点可能是
322
00:21:15,390 --> 00:21:18,900
能向这门语言提一些基于某些事实集合的问题,
323
00:21:18,900 --> 00:21:21,330
真的很好。
324
00:21:21,330 --> 00:21:26,060
现在我们开始,先制作一些事实的集合。
325
00:21:26,060 --> 00:21:31,700
这是一家位于波士顿的高科技公司的员工记录的一部分。
326
00:21:34,440 --> 00:21:37,500
这是Ben Bitdiddle的记录。
327
00:21:37,500 --> 00:21:41,470
Ben Bitdiddle是计算机巫师。
328
00:21:41,470 --> 00:21:44,660
薪水超低的计算机巫师。
329
00:21:46,420 --> 00:21:49,330
他的领导是Oliver Warbucks
330
00:21:49,330 --> 00:21:52,150
这是他的地址。
331
00:21:52,150 --> 00:21:55,220
我们记录信息的格式是这样的:
332
00:21:55,220 --> 00:21:57,300
职位,薪资,上级,地址。
333
00:21:57,300 --> 00:21:59,250
我们还有一些约定。
334
00:21:59,250 --> 00:22:01,570
这里的Computer表示Ben在计算机部门工作,
335
00:22:01,570 --> 00:22:03,590
它在计算机部门的岗位
336
00:22:03,590 --> 00:22:06,440
是巫师。
337
00:22:06,440 --> 00:22:07,580
这是另外一个人。
338
00:22:07,580 --> 00:22:13,860
Alyssa, Alyssa P. Hacker是一个程序员,
339
00:22:13,860 --> 00:22:17,550
在Ben手底下工作,她住在剑桥。
340
00:22:17,550 --> 00:22:19,990
Ben手下还有另外一个程序员,
341
00:22:19,990 --> 00:22:22,820
Lem E. Tweakit。
342
00:22:22,820 --> 00:22:26,330
这是另外一个程序员教练,
343
00:22:26,330 --> 00:22:30,100
Louis Reasoner,为Alyssa工作。
344
00:22:30,100 --> 00:22:34,830
公司的大老板,
345
00:22:34,830 --> 00:22:37,010
谁也管不了他,对吧?
346
00:22:37,010 --> 00:22:38,110
Oliver Warbucks。
347
00:22:38,110 --> 00:22:43,080
好了,我们要做的就是
348
00:22:43,080 --> 00:22:44,971
围绕着这个小世界开始提问。
349
00:22:44,971 --> 00:22:47,410
这就是我们要进行逻辑演算的
350
00:22:47,410 --> 00:22:48,660
小世界。
351
00:22:51,420 --> 00:22:55,810
让我在这里写一下,最后一次,
352
00:22:55,810 --> 00:22:57,600
你们应该从这们课里学到的最重要的东西
353
00:22:57,600 --> 00:23:00,760
当有人问到你们某一门语言时,
354
00:23:00,760 --> 00:23:03,440
你能说,好吧 --
355
00:23:03,440 --> 00:23:15,050
它的原语是什么,组合的方式是什么,
356
00:23:15,050 --> 00:23:18,480
你怎么把原语组合起来,
357
00:23:18,480 --> 00:23:24,690
如何进行抽象,如何对复合的零件进行抽象,
358
00:23:24,690 --> 00:23:26,740
以便可以把它们当作零件,来搭建更复杂的东西?
359
00:23:28,500 --> 00:23:31,440
这些我们已经讲了很多次了,
360
00:23:31,440 --> 00:23:32,690
但还是值得再说一次。
361
00:23:36,210 --> 00:23:36,670
开始。
362
00:23:36,670 --> 00:23:38,040
原语。
363
00:23:38,040 --> 00:23:41,660
好吧,事实上原语只有一条,
364
00:23:41,660 --> 00:23:44,400
叫做查询。
365
00:23:44,400 --> 00:23:46,810
查询原语。
366
00:23:46,810 --> 00:23:48,060
让我们看一些查询原语的例子。
367
00:23:52,160 --> 00:23:53,100
Job x.
368
00:23:53,100 --> 00:23:55,550
谁是程序员?
369
00:23:55,550 --> 00:24:04,700
或者查找所有符合以下模式的事实:
370
00:24:04,700 --> 00:24:06,640
Job of the x is computer programmer.
371
00:24:06,640 --> 00:24:08,470
这里有一点小小的语法了。
372
00:24:08,470 --> 00:24:11,330
不带问号的东西是字面量,
373
00:24:11,330 --> 00:24:13,940
问号 x表示变量,这个东西会匹配
374
00:24:13,940 --> 00:24:18,110
这样的事实:Alyssa P. Hacker是一个程序员,
375
00:24:18,110 --> 00:24:21,930
或者x是Alyssa P. Hacker。
376
00:24:26,820 --> 00:24:29,170
或者更一般的,我们可以在
377
00:24:29,170 --> 00:24:30,750
一条语句里包含两个变量。
378
00:24:30,750 --> 00:24:39,530
我可以这样写:x的工作是computer XXX.
379
00:24:39,530 --> 00:24:42,140
这会匹配计算机巫师。
380
00:24:42,140 --> 00:24:44,865
注意这里:类型匹配巫师,
381
00:24:44,865 --> 00:24:49,390
或者程序员,或者x可以匹配
382
00:24:49,390 --> 00:24:50,370
很多东西。
383
00:24:50,370 --> 00:24:53,270
所以在我们的例子里,
384
00:24:53,270 --> 00:24:55,150
数据库里只有3条事实符合查询。
385
00:24:59,210 --> 00:25:04,910
我们看一下同样的查询。这是为了给你们看一下语法。
386
00:25:04,910 --> 00:25:11,490
这个查询不匹配job of x,
387
00:25:11,490 --> 00:25:13,200
不匹配Lewis Reasoner。原因是
388
00:25:13,200 --> 00:25:17,160
当我这样写的时候,表示这里有两个符号,
389
00:25:17,160 --> 00:25:22,730
第一个词是computer,
390
00:25:22,730 --> 00:25:24,810
第二个可以是任何东西。
391
00:25:24,810 --> 00:25:28,130
Lewis的职位描述有三个符号,
392
00:25:28,130 --> 00:25:30,340
所以不匹配。
393
00:25:30,340 --> 00:25:35,360
再给你们看一点语法,
394
00:25:35,360 --> 00:25:37,920
我想写的更一般类型是一个东西
395
00:25:37,920 --> 00:25:42,550
后面加一个点,这中方法表示
396
00:25:42,550 --> 00:25:46,560
这回死一个list,其中第一个元素
397
00:25:46,560 --> 00:25:49,350
是computer,其它的东西,
398
00:25:49,350 --> 00:25:50,600
我称之为类型。
399
00:25:53,730 --> 00:25:56,930
所以这一个是匹配的。
400
00:25:56,930 --> 00:26:00,000
Lewis's 的工作是程序员教练,
401
00:26:00,000 --> 00:26:04,690
这里的类型是list的身体部分,就是
402
00:26:04,690 --> 00:26:06,960
程序员教练列表。
403
00:26:06,960 --> 00:26:08,410
像这样对“点”的处理会
404
00:26:08,410 --> 00:26:10,460
由Lisp解释器自动完成。
405
00:26:15,900 --> 00:26:17,760
好了,让我们正式试一试吧。
406
00:26:17,760 --> 00:26:20,810
我用这门语言进行输入,
407
00:26:20,810 --> 00:26:23,630
然后答案就会出来。
408
00:26:23,630 --> 00:26:25,180
看这。
409
00:26:25,180 --> 00:26:30,000
我来问:谁在计算机部门工作?
410
00:26:30,000 --> 00:26:39,730
Job of x is computer dot y.
411
00:26:39,730 --> 00:26:42,562
这个变量叫什么没关系。
412
00:26:42,562 --> 00:26:45,690
答案出来了。有4个答案。
413
00:26:48,650 --> 00:26:51,380
我再问:告诉我所有人的领导。
414
00:26:52,505 --> 00:26:56,610
我这样写查询,一条查询原语,
415
00:26:56,610 --> 00:26:59,390
the supervisor of x is y.
416
00:27:02,860 --> 00:27:05,540
这就是所有我们能了解的上下级关系。
417
00:27:05,540 --> 00:27:08,830
我还可以输入:谁住在剑桥?
418
00:27:08,830 --> 00:27:20,670
像这样:address of x is cambridge . 随便什么
419
00:27:25,090 --> 00:27:26,585
只有一个人住在剑桥。
420
00:27:30,820 --> 00:27:32,170
这些都是查询原语。
421
00:27:32,170 --> 00:27:34,460
和系统的间的基本交互就是
422
00:27:34,460 --> 00:27:38,140
你输入查询,系统
423
00:27:38,140 --> 00:27:39,620
输出所有可能的答案。
424
00:27:39,620 --> 00:27:43,100
或者说,系统找出
425
00:27:43,100 --> 00:27:45,330
所有x和y或者t或者随便什么名字的变量的所有可能的取值
426
00:27:45,330 --> 00:27:50,380
,并按所有满足查询的方式
427
00:27:50,380 --> 00:27:53,080
填充原语并输出 ---
428
00:27:53,080 --> 00:27:56,250
回想一下系统规则那堂课里讲的--
429
00:27:56,250 --> 00:27:59,150
用所有变量的所有可能的取值实例化查询
430
00:27:59,150 --> 00:28:01,000
并输出。
431
00:28:01,000 --> 00:28:02,370
对一门逻辑语言,
432
00:28:02,370 --> 00:28:03,350
存在很多种排列方式
433
00:28:03,350 --> 00:28:06,010
比如Prolog就略有差异。
434
00:28:06,010 --> 00:28:08,980
它不是输出查询语句,而是输出
435
00:28:08,980 --> 00:28:12,230
x等于这个,y等于这个,
436
00:28:12,230 --> 00:28:12,650
或者x=这个,y=这个。
437
00:28:12,650 --> 00:28:16,430
这些是一些表层的东西,你可以
438
00:28:16,430 --> 00:28:19,070
选择你喜欢的方式。
439
00:28:19,070 --> 00:28:20,760
好。
440
00:28:20,760 --> 00:28:21,340
就这样。
441
00:28:21,340 --> 00:28:23,390
这门语言里有哪些原语?
442
00:28:23,390 --> 00:28:24,570
就一个,对吗?
443
00:28:24,570 --> 00:28:27,230
查询原语。
444
00:28:31,360 --> 00:28:31,650
好。
445
00:28:31,650 --> 00:28:34,330
组合的方法。
446
00:28:34,330 --> 00:28:39,770
我们看一些这门语言里的复合查询。
447
00:28:39,770 --> 00:28:41,790
这里有一个。
448
00:28:41,790 --> 00:28:47,250
它说:告诉我所有在
449
00:28:47,250 --> 00:28:49,810
计算机部门工作的人。
450
00:28:49,810 --> 00:28:52,610
告诉我所有在计算机部门工作
451
00:28:52,610 --> 00:28:53,860
的人,以及他们的领导。
452
00:28:56,800 --> 00:29:00,220
我写这条语句的方式是这样的: .. and ...
453
00:29:00,220 --> 00:29:04,920
而且x是computer或者别的什么东西。
454
00:29:04,920 --> 00:29:07,560
而且x的职位是computer . y。
455
00:29:07,560 --> 00:29:11,650
x的主观是z。
456
00:29:11,650 --> 00:29:13,570
告诉我所有在计算机部门工作的人--
457
00:29:13,570 --> 00:29:16,460
就是这个--以及他们的领导。
458
00:29:16,460 --> 00:29:20,290
注意在这条查询里我使用了3个变量--
459
00:29:20,290 --> 00:29:23,660
x, y, 和z.
460
00:29:23,660 --> 00:29:29,450
这个x和这个x相同。
461
00:29:29,450 --> 00:29:31,560
所以x在计算机部门工作,
462
00:29:31,560 --> 00:29:34,810
而且x的领导是z。
463
00:29:34,810 --> 00:29:37,250
我们再试一下另外一个。
464
00:29:37,250 --> 00:29:39,005
所以一种组合的方式是 and。
465
00:29:41,540 --> 00:29:45,790
都有哪些人收入超过3万美元?
466
00:29:45,790 --> 00:29:51,640
员工p的收入是a。
467
00:29:54,590 --> 00:30:00,600
我们再看一下a,a大于3万美元。
468
00:30:00,600 --> 00:30:06,300
lisp-value在这的作用是
469
00:30:06,300 --> 00:30:10,600
作为查询语言和底层LISP的接口。
470
00:30:10,600 --> 00:30:13,540
lisp-value可以允许你在一条查询中
471
00:30:13,540 --> 00:30:17,180
调用任何LISP谓词。
472
00:30:17,180 --> 00:30:19,110
我在这使用了LISP谓词greater than,
473
00:30:19,110 --> 00:30:21,020
所以我用lisp-value。
474
00:30:21,020 --> 00:30:21,750
我这样写。
475
00:30:21,750 --> 00:30:28,190
所有收入超过3万美元的人。
476
00:30:28,190 --> 00:30:31,270
或者这里有一个跟复杂的。
477
00:30:31,270 --> 00:30:36,150
告诉我所有这样的人:他在计算机部工作,
478
00:30:36,150 --> 00:30:38,560
但他的领导不在
479
00:30:38,560 --> 00:30:39,810
计算机部。
480
00:30:42,790 --> 00:30:45,510
x在计算机部。
481
00:30:45,510 --> 00:30:47,780
The job of x is computer
dot y. x的工作是computer 点。
482
00:30:47,780 --> 00:30:55,570
and 不能满足这个条件:
483
00:30:55,570 --> 00:30:59,620
x有领导z,而且z的工作是computer加随便什么东西。
484
00:30:59,620 --> 00:31:04,050
好的,这一次这个x还是和这个x相等。
485
00:31:04,050 --> 00:31:05,710
这个z和这个z相等。
486
00:31:09,390 --> 00:31:11,380
你们看到了另外一种组合的方式:not(非)。
487
00:31:17,272 --> 00:31:20,880
好的,让我们再看一下。
488
00:31:20,880 --> 00:31:22,400
它工作的方式是一样的。
489
00:31:22,400 --> 00:31:33,110
我可以对机器说 x的工作是computer点y。
490
00:31:33,110 --> 00:31:35,400
computer dot y.
491
00:31:38,480 --> 00:31:46,600
x的领导是z。
492
00:31:46,600 --> 00:31:50,794
我把这些作为查询输进去。
493
00:31:50,794 --> 00:31:55,680
得到的输出是使用所有可能的答案
494
00:31:55,680 --> 00:31:58,930
填充输进去的查询。
495
00:31:58,930 --> 00:32:02,000
你们看这里有很多个答案。
496
00:32:02,000 --> 00:32:02,190
好吧。
497
00:32:02,190 --> 00:32:05,230
所以在这门语言里的组合方式是逻辑运算。
498
00:32:05,230 --> 00:32:07,550
这也是为什么它被称为逻辑语言。
499
00:32:09,800 --> 00:32:16,120
组合的方式是 AND和NOT。
500
00:32:16,120 --> 00:32:18,490
还有一个我还没讲的,OR(或)。
501
00:32:18,490 --> 00:32:24,310
我还介绍了lisp-value。
502
00:32:24,310 --> 00:32:26,365
当然它不是逻辑运算,而是一个小技巧,用于
503
00:32:26,365 --> 00:32:29,250
和LISP对接以便获得更多的功能。
504
00:32:29,250 --> 00:32:32,690
这些就是组合的方式。
505
00:32:32,690 --> 00:32:34,160
OK。 抽象的方式。
506
00:32:34,160 --> 00:32:35,410
我们想要做的是--
507
00:32:38,330 --> 00:32:42,260
我们往回看上一张胶片。
508
00:32:42,260 --> 00:32:45,010
我们的问题很复杂,
509
00:32:45,010 --> 00:32:48,800
那些在一个部门工作,
510
00:32:48,800 --> 00:32:52,400
但是领导却不在这个部门的人。
511
00:32:52,400 --> 00:32:56,090
和之前一样,给它起个名字。
512
00:32:56,090 --> 00:32:58,800
如果一个人在某部门工作,但他的
513
00:32:58,800 --> 00:33:00,950
领导却不在这个部门工作,
514
00:33:00,950 --> 00:33:02,750
我们就叫他大腕(big shot)。
515
00:33:02,750 --> 00:33:08,370
我们定义一条规则,x满足一下条件时是一个大腕:
516
00:33:08,370 --> 00:33:16,760
x在某部门工作,同时x的领导
517
00:33:16,760 --> 00:33:19,610
在同一部门工作不成立。
518
00:33:21,510 --> 00:33:22,940
这就是我们的抽象方式。
519
00:33:22,940 --> 00:33:24,190
这是一条规则。
520
00:33:26,220 --> 00:33:27,580
一条规则有3个部分。
521
00:33:30,970 --> 00:33:33,450
这个规定了它是一条规则。
522
00:33:33,450 --> 00:33:37,530
这是规则的结论。
523
00:33:37,530 --> 00:33:40,000
这是规则的主体。
524
00:33:40,000 --> 00:33:42,150
你们可以把这个当作一条逻辑:
525
00:33:42,150 --> 00:33:46,940
如果规则的主体为真,则
526
00:33:46,940 --> 00:33:49,470
规则的结论部分也为真。
527
00:33:49,470 --> 00:33:52,640
想要得出x是一个大腕的结论,
528
00:33:52,640 --> 00:33:57,480
验证它就可以了。
529
00:33:57,480 --> 00:33:58,820
规则就是长这个样子的。
530
00:34:03,280 --> 00:34:07,180
再回去看我们休息之前我给出的
531
00:34:07,180 --> 00:34:08,110
合并的例子。
532
00:34:08,110 --> 00:34:11,610
我们看一下如果用规则来表示会是什么样子。
533
00:34:11,610 --> 00:34:14,030
我要把我给出的逻辑
534
00:34:14,030 --> 00:34:15,500
转换成一组像这样的格式的规则。
535
00:34:18,739 --> 00:34:19,350
这是一条规则。
536
00:34:19,350 --> 00:34:21,710
记住,这是“合并成为”。
537
00:34:21,710 --> 00:34:28,489
这里有一条规则:空list和y合并
538
00:34:28,489 --> 00:34:29,620
的结果是y。
539
00:34:29,620 --> 00:34:30,870
这是规则的结论。
540
00:34:33,210 --> 00:34:36,650
注意这条规则没有主体部分。
541
00:34:36,650 --> 00:34:40,010
在这门语言里,一条没有主体的规则
542
00:34:40,010 --> 00:34:41,239
永远为真。
543
00:34:41,239 --> 00:34:42,510
你可以始终认为它为真。
544
00:34:45,190 --> 00:34:47,530
这里是另外一条逻辑,它说空list中的anything
545
00:34:47,530 --> 00:34:49,460
合并得到的结果是anything。
546
00:34:49,460 --> 00:34:50,900
就是这个。
547
00:34:50,900 --> 00:34:55,510
规则:y和空list合并得到y。
548
00:34:55,510 --> 00:34:58,060
这对应于我们的合并过程中的最后两个case。
549
00:34:58,060 --> 00:35:00,890
但我们现在说的是规则,而不是过程。
550
00:35:03,490 --> 00:35:07,560
然后我们还有另外一条规则:如果你知道
551
00:35:07,560 --> 00:35:09,830
短一点的东西如何合并,你就可以把它们放到一起。
552
00:35:09,830 --> 00:35:15,340
这就是说,如果你有一个list x,y,和z,而且
553
00:35:15,340 --> 00:35:19,530
你想知道a.x--这表示常量a加上x,或者
554
00:35:19,530 --> 00:35:23,160
一个list,第一个元素是a,剩余部分是x --
555
00:35:23,160 --> 00:35:26,230
如果你想得到它: a.x和b.y合并
556
00:35:26,230 --> 00:35:27,480
得到b.c。
557
00:35:30,570 --> 00:35:34,070
就是说你合并list a.x和b.x并且
558
00:35:34,070 --> 00:35:37,680
你将得到一个以b开始的东西--
559
00:35:37,680 --> 00:35:41,880
如果你知道,
560
00:35:41,880 --> 00:35:48,690
a.x和y合并得到z,以及a大于b同时成立。
561
00:35:48,690 --> 00:35:52,610
于是当我们合并它们时,b会在list的前面。
562
00:35:52,610 --> 00:35:56,050
这是从我前面用伪英语描述
563
00:35:56,050 --> 00:35:57,960
的逻辑翻译过来的。
564
00:35:57,960 --> 00:35:59,870
为了更完备的描述这个问题,
565
00:35:59,870 --> 00:36:03,130
再看另一种情况。
566
00:36:03,130 --> 00:36:08,170
如果x和b.y合并等于z而且b大于a,则
567
00:36:08,170 --> 00:36:12,190
a.x和b.y合并等于a.z。
568
00:36:12,190 --> 00:36:15,610
这就是我用这门语言写的一个小程序,
569
00:36:15,610 --> 00:36:17,416
我们看一下它运行起来是什么样子。
570
00:36:21,900 --> 00:36:27,740
我把前面的合并规则输进去,
571
00:36:27,740 --> 00:36:28,510
并把它做为一个过程使用。
572
00:36:28,510 --> 00:36:39,590
我可以说:合并1,3和2,7。
573
00:36:39,590 --> 00:36:43,330
我现在就像一个lisp过程一样使用它。
574
00:36:43,330 --> 00:36:46,940
它现在需要思考一下,
575
00:36:46,940 --> 00:36:48,190
并应用这些规则。
576
00:36:50,780 --> 00:36:52,800
它找到了一个答案。
577
00:36:52,800 --> 00:36:55,370
它还要再找一找有没有别的答案。
578
00:36:55,370 --> 00:36:57,810
它事先并不知道只有一个答案。
579
00:36:57,810 --> 00:37:00,790
所以它正在检查所有的可能性,
580
00:37:00,790 --> 00:37:01,970
然后它回答:没有别的了。
581
00:37:01,970 --> 00:37:02,775
完。
582
00:37:02,775 --> 00:37:05,210
这里我像使用过程一样使用这些规则。
583
00:37:05,210 --> 00:37:08,340
记住全部的要点在于,
584
00:37:08,340 --> 00:37:10,220
我可以问不同形式的问题。
585
00:37:10,220 --> 00:37:24,590
我可以问,比如,2和a合并。
586
00:37:24,590 --> 00:37:29,440
有些2元素列表我已经知道以2开始,
587
00:37:29,440 --> 00:37:34,600
其它的我不知道,而且x和某些别的list
588
00:37:34,600 --> 00:37:39,510
合并得到a, 1, 2, 3, 4。
589
00:37:42,760 --> 00:37:44,590
它又在开始思考了。
590
00:37:44,590 --> 00:37:45,840
它会找到答案的 --
591
00:37:48,070 --> 00:37:49,095
它找到了一个。
592
00:37:49,095 --> 00:37:53,830
它说a可以是3,x是list 1, 4.
593
00:37:53,830 --> 00:37:57,220
现在它还要继续查找,因为
594
00:37:57,220 --> 00:37:59,050
它事先并不知道
595
00:37:59,050 --> 00:38:00,300
并没有其它的可能性了。
596
00:38:03,680 --> 00:38:10,660
我前面说过,我可以问一些类似合并的问题:
597
00:38:10,660 --> 00:38:17,275
比如,谁和谁合并得到1,2,3,4,5?
598
00:38:24,340 --> 00:38:25,590
它正在思考。
599
00:38:28,490 --> 00:38:30,310
它应该会找到很多个答案。
600
00:38:35,180 --> 00:38:37,920
你们看到了,你们需要付出
601
00:38:37,920 --> 00:38:39,170
运行速度作为代价。
602
00:38:42,210 --> 00:38:43,880
有3个原因。
603
00:38:43,880 --> 00:38:47,630
首先这门语言被解释了两次。
604
00:38:47,630 --> 00:38:50,100
在现实环境中,你们会
605
00:38:50,100 --> 00:38:52,190
把它编译成原子操作。
606
00:38:52,190 --> 00:38:56,410
另一个原因是我们用的合并算法
607
00:38:56,410 --> 00:38:58,380
是双重递归的。
608
00:38:58,380 --> 00:39:01,020
所以它会占用很长的时间。
609
00:39:01,020 --> 00:39:06,710
最后,它会跑完并找到--
610
00:39:06,710 --> 00:39:07,130
找到什么?
611
00:39:07,130 --> 00:39:08,730
第2到第5个可能的答案。
612
00:39:12,140 --> 00:39:14,830
你们看,它们的顺序是相当随机的,
613
00:39:14,830 --> 00:39:17,100
这取决于它按什么顺序
614
00:39:17,100 --> 00:39:20,160
去试验这些规则。
615
00:39:20,160 --> 00:39:21,530
实际上,当我们编辑视频时,
616
00:39:21,530 --> 00:39:24,310
会把这块的速度加快。
617
00:39:24,310 --> 00:39:26,600
你们自己做demo的时候
618
00:39:26,600 --> 00:39:28,250
也会想要把这些等待时间刨掉吧?
619
00:39:32,840 --> 00:39:34,260
它还在那里推磨呢。
620
00:39:39,220 --> 00:39:41,170
一共有32中可能呢--
621
00:39:41,170 --> 00:39:42,630
我们不等它把所有的答案都打出来了。
622
00:39:47,850 --> 00:39:49,410
所以在这门语言里,
623
00:39:49,410 --> 00:39:50,660
抽象的方法是规则。
624
00:39:53,630 --> 00:39:57,410
我们把一堆用逻辑联系在一起的东西
625
00:39:57,410 --> 00:40:00,350
起个名字。
626
00:40:00,350 --> 00:40:02,080
你们可以把这看作是为一个
627
00:40:02,080 --> 00:40:03,410
特定的逻辑模式起名。
628
00:40:03,410 --> 00:40:05,810
或者可以把它看作是这样的:如果你想要得到某些结论,
629
00:40:05,810 --> 00:40:10,660
你可以应用那些逻辑的规则。
630
00:40:10,660 --> 00:40:13,420
这些是这门语言的三个元素。
631
00:40:13,420 --> 00:40:15,670
我们休息一下,接下来我们将要讲
632
00:40:15,670 --> 00:40:16,920
如何实现它。
633
00:40:22,747 --> 00:40:27,380
学生: 使用lisp-value一类的东西
634
00:40:27,380 --> 00:40:31,770
是否会影响我们在一个查询的两个方向间的转换?
635
00:40:31,770 --> 00:40:33,530
教授: OK, 这是一个--
636
00:40:33,530 --> 00:40:37,840
这个问题是,使用lisp-value是否会干扰
637
00:40:37,840 --> 00:40:40,090
在查询的两个方向上转换的能力?
638
00:40:40,090 --> 00:40:43,850
我们还没有讲到具体的实现,
639
00:40:43,850 --> 00:40:46,890
但答案是“yes”,会影响的。
640
00:40:46,890 --> 00:40:50,510
一般的,我们在后面会看到的 --
641
00:40:50,510 --> 00:40:53,330
尽管我不会进入具体的细节 --
642
00:40:53,330 --> 00:40:58,140
这确实很复杂,尤其是当你们使用
643
00:40:58,140 --> 00:40:59,780
not(非)或lisp-value时--
644
00:40:59,780 --> 00:41:04,310
或者说,实际上如果你们想使用任何“AND”以外的东西时,
645
00:41:04,310 --> 00:41:07,350
都会变得很复杂,很难判断
646
00:41:07,350 --> 00:41:08,700
这些东西好不好使。
647
00:41:08,700 --> 00:41:10,360
我们不能保证他们在所有的情况下都能正常工作。
648
00:41:10,360 --> 00:41:14,300
今天的后半堂课快结束时我会将这个问题。
649
00:41:14,300 --> 00:41:17,180
但你的问题的答案是:yes。
650
00:41:17,180 --> 00:41:22,000
当我们通过lisp-value获得了更多的能力时,
651
00:41:22,000 --> 00:41:24,170
我们也失去了一些逻辑编程的关键能力。
652
00:41:24,170 --> 00:41:28,090
这是一个必须接受的交换。
653
00:41:28,090 --> 00:41:30,390
OK。我们休息一下。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。