lisp初体验-Practical Common Lisp笔记-4.迷你数据库-中

额,先简单申明下,由于本站code不支持lisp,而原先的代码着实不利于排版(太丑了),这里凡是代码部分都用python的格式,凡是输出部分都用java的格式~

作为一个数据库,纵然再mini,也得要有最基本的功能:增删改查,本篇就逐一加上。

[b]查[/b]
作为lisp代码,如果查找能类似这样:
(select :artist "Dixie Chicks")

还是蛮不错的。自然,万能的宏已经准备好了类似的动作:REMOVE-IF-NOT,遍历列表,如果不符合要求则删除(不要担心,这里只是个副本,不会影响原数据),显示出的自然就是咱要的东西了。
试试看:
代码:
(remove-if-not #'evenp '(1 2 3 4 5 6 7 8 9 10))

结果:
(2 4 6 8 10)

evenp也是宏,用于取偶,这里还有个有趣的东东:#'符号,用来表明,后面是个函数,不是变量哦(注意是#和'两个符号组合,后面还有一个单引号',那是和后面的列表一起的,表示那是个列表)。
上面的代码还有另一种写法:
 (remove-if-not #'(lambda (x) (= 0 (mod x 2))) '(1 2 3 4 5 6 7 8 9 10))

这里用了匿名函数:
(lambda (x) (= 0 (mod x 2)))

加上用于比对字符串的默认函数:equal,便得到了搜索语句:

(remove-if-not
#'(lambda (cd) (equal (getf cd :artist) "Dixie Chicks")) *db*)

封装下:

(defun select-by-artist (artist)
(remove-if-not
#'(lambda (cd) (equal (getf cd :artist) artist))
*db*))

这里用到的匿名函数似乎有局限,总不能每一个字段都写一套吧,那么这样呢:

(defun select (selector-fn)
(remove-if-not selector-fn *db*))

然后:

(select #'(lambda (cd) (equal (getf cd :artist) "Dixie Chicks")))

你没有看错,这里将匿名函数作为参数来传递,当然普通函数也可以这样做:

(defun artist-selector (artist)
#'(lambda (cd) (equal (getf cd :artist) artist)))
(select (artist-selector "Dixie Chicks"))

哪种比较好就见仁见智了。不过,似乎还是比较麻烦,整体上就字段有差异,不至于要写这么多重复的东西吧,让咱先跳出这个问题,看看这个:

(defun foo (a b c) (list a b c))
(defun foo (&key a b c) (list a b c))

两者的区别在于第一个必须参数全部输入,而第二个则不。
例如:
(foo :a 1 :c 3) ==> (1 NIL 3)

既然允许这么传参,那么自然也就允许默认值了:

(defun foo (&key a (b 20) (c 30 c-p)) (list a b c c-p))

注意:这里的c-p是用来确认c是否是有传入值,有则为t(真),标准格式:x-p
测试:

(foo :a 1 :b 2 :c 3) ==> (1 2 3 T)
(foo :c 3 :b 2 :a 1) ==> (1 2 3 T)
(foo :a 1 :c 3) ==> (1 20 3 T)
(foo) ==> (NIL 20 30 NIL)

如此,最终的查询语句应该是这个样子:

(select (where :artist "Dixie Chicks"))

where函数:

(defun where (&key title artist rating (ripped nil ripped-p))
#'(lambda (cd)
(and
(if title (equal (getf cd :title) title) t)
(if artist (equal (getf cd :artist) artist) t)
(if rating (equal (getf cd :rating) rating) t)
(if ripped-p (equal (getf cd :ripped) ripped) t))))

至此,查询功能算完结了。

[b]改[/b]
理论上来说,完成了where函数后,更新操作应该不难:

(defun update (selector-fn &key title artist rating (ripped nil ripped-p))
(setf *db*
(mapcar
#'(lambda (row)
(when (funcall selector-fn row)
(if title (setf (getf row :title) title))
(if artist (setf (getf row :artist) artist))
(if rating (setf (getf row :rating) rating))
(if ripped-p (setf (getf row :ripped) ripped)))
row) *db*)))

这里主要用到默认宏:mapcar,用于遍历db,funcall没有给出说明。这条算目前为止逻辑最复杂的函数了。主体是遍历出所有数据,有更新的换成新数据,然后重新覆盖原来的db(作为一个数据库,动作有点大)。
用法:
(update (where :artist "Dixie Chicks") :rating 11)


[b]删[/b]
相比改,删除似乎更加容易:

(defun delete-rows (selector-fn)
(setf *db* (remove-if selector-fn *db*)))

至此,似乎全部ok了,且慢,难道不觉得有些缺憾么?

(未完待续)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值