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

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

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


作为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))))

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


理论上来说,完成了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)



相比改,删除似乎更加容易:
(defun delete-rows (selector-fn)
  (setf *db* (remove-if selector-fn *db*)))

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

(未完待续)

你可能感兴趣的:(C++,c,python,C#,lisp)