Start
最近一直在阅读《如何阅读一本书》,突然有了一个新想法–用新的方式写读书笔记。
虽说我原来也写过读书笔记,但基本是把书中的一些重点总结出来,或者是摘抄一些经典的句子,从来没想过将一本书的阅读过程写成博客,不是简单的读书思考,发表自己的看法,而是去抓住作者的意图,像是批注,又像是总结,同时又将自己已积累的知识串入。这对自己文笔也是一次锻炼-能够把问题描述清楚不容易。
之所以这样,是为了使用《如何阅读一本书》的读书技巧,理解作者的意图。
第三章: 简单的数据库
这一章,作者通过实现了一个能够增删改查的单表数据库,来介绍common lisp的一些特性和优点。
值得一提的是,作者在这一章就介绍了不少lisp知识,而且每当引入新的lisp知识时,作者会写这样一句话:”目前我不会深入讨论关于这一符号的所有细节”。可以看出,作者是一个实用主义,不会先空谈大量的理论。
作者先定义,将一条CD记录用如下的方式表示和存储:
1 | (list :title title :artist artist :rating rating :ripped ripped) |
创建一条记录和插入一条记录的方式都比较简单,为了让打印出来的文章变得漂亮,作者在之后的篇幅里介绍了Format和一些格式化字符串的方式。
我觉得这些都不算是重点,即使语法上不同,其本质思想也与其它语言,比如C的printf
之类的格式化字符串类似。
之后加入了交互式的输入方式。引入了loop
宏,来让用户多次输入。
loop
宏配上if return
类似于while
if break
,
and or
也与python类似,都是短路求值,都是返回最后一个符合的布尔表达式,只不过语法不同。
之后为了持久化,引入了读写文件
这里有个有趣的地方是,lisp可以直接将这个列表写入文件,然后读取出来,它不需要像其它语言,比如python那样,将一个对象序列化之后,才能写入文件。
接下来作者引入了查询:
1 | (remove-if-not #'(lambda(x)(= 1 (mod x 2))) '(1 2 3 4 5 6 7 8 9)) |
这样的方式是告诉lisp,将这个符号当作一个函数,有点类似于其它语言中的函数对象。
这种风格,应该是函数式的编程,就像是python中的filter:
1 | filter(lambda x: x%2 == 0, range(1, 11)) |
首先,这是一个通过artist查找的select函数:
1 | (defun select-by-artist (artist) |
很明显,你不得不为每一个字段写一个单独的select函数,比如select-by-title
,select-by-rating
。
为了防止重复,作者进行了第一次抽象,
1 | (defun select (select-fn) |
具体的选择函数在根据不同的字段去实现。
这其实很像是面向对象中的定义接口。然后在根据接口做不同的实现。
虽然要写的代码比原来少了,但是你还是要为不同的字段实现不同的选择器,比如
1 | (defun atrist-selector (artist) |
接下来,作者实现了一个函数,能够根据参数生成选择器。
也就似乎where函数的实现:
where的第一次实现
其基本思路就是用and,将传入参数的字段拼接起来,没有传入参数的,就用t替换。
这种方法其实没什么高深的,在python中,用闭包将选择生成的lamaba表达式放入一个list,一样可以做得到。
主要是因为lisp的所有代码都是list表达式,所以看着短,写起来也自由的多。
update的引入
因为有了之前的铺垫,所以update看上去也没那么复杂。
通过mapcar
(有点类似于python中的map),对每一个行执行where函数,如果为真,就通过传入的参数,执行相应的更新。
有了select,delete本身就简单多了:
1 | (defun delete-rows (selector-fn) |
接下来介绍的部分,基本算是这一章的亮点了。
这也是lisp的精髓之一,宏:
新版的where
原来的where,还是沉余,因为你要在运行期间,检查某个参数是否传了进来,而且如果我们加入了新的字段,也要对where和updater进行大量的修改。
作者用一个reverse的列子,介绍了宏,这个例子没有多大意义,就连作者都称其为“简单而荒唐”的例子。
作者在这里为宏下了一个定义:“一个由编译器自动为你运行的代码生成器”。
也就是说,我们可以通过宏,将这段代码:
1 | (where :title "Give Us a Break" :ripped ) |
直接”变为”如下代码:
1 | #'(lambda (cd) |
先是:
1 | (defun make-comparison-expr (field value) |
这里引入了反引号,进行代码精简。
1 | (defun make-comparison-expr (field value) |
它可以通过传入的字段,生成语句。
之后是loop宏,它将传入的参数两个两个的传入到 make-comparison-expr
中,将结果保存到一个list中:
1 | (defun make-comparisons-list (fields) |
之后才是where宏,它将上面函数返回的list用and拼接。
1 | (defmacro where (&rest clauses) |
这里引入了两个符号:@和&rest:
- @用来展开一个list,
- &rest,类似于python中的*args,
感想:
- 说实话,lisp的语法有些太“疯狂”了,不知道要花多长时间才能够真正理解。
- 但是我确实可以细微的体会到lisp的强大之处。
- 我有一种预感,但是不知道对不对。即:优化,精简代码的大多数场景,都是将if等逻辑判断,改为数据结构驱动。