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

[color=red]注意:[/color]如果你是初学者,甚至都没有其他任何语言基础,那么此篇,即<迷你数据库下>,可以略过,作为前两篇(上、中)的总结、优化,固然很强大,但个人感觉极有可能把初学者带到沟里去,这里涉及到一些较深的东西,并且作者没有给出详解(一如既往的"后面篇章会有的")。so,阅读需谨慎,此篇可跳过~

好吧,如果上面的红字没有能够阻止你的话,就让我们开始。

[b]让你的代码变得更简洁[/b]

虽然之前的数据库也算基本可用了,不过明眼人估计都看出来有很多代码冗余的存在,比如where中的这句:
(if title (equal (getf cd :title) title) t)

在功用上,为了能够匹配所有情况而不得不每一次都做多重判定(浪费了效能),在代码上,除了关键字的区别外,根本是一个模子啊,那么为什么不就用同一个模子来做呢。
咱的期望是,如果要如此查找:
(select (where :title "Give Us a Break" :ripped t))

那么实现的代码最终看起来便是这个样子:

(select
#'(lambda (cd)
(and (equal (getf cd :title) "Give Us a Break")
(equal (getf cd :ripped) t))))

只判断提供的东西。
其实,用lisp是非常容易达到这个目标的,不过这里就得涉及到lisp中的大杀器"宏"了,之前曾经简单解释成"当成公用函数",事实当然并非如此。宏即是宏,函数即是函数(有点绕)。不过在lisp中使用方式倒是差不多。目前也只需要知其形,至于意么,就见仁见智了~
跳出数据库,在这里简单建个宏,用用看:
(defmacro backwards (expr) (reverse expr))

宏和函数的定义关键字是不同的。这里reverse是自带宏,作用是:
(reverse '(1 2 3)) ==> (3 2 1)

用下咱写的宏:
(backwards ("hello, world" t format))

结果:

hello, world
NIL

是不是理解这个宏的用法了,此宏将lisp语法逆向了,如果你尝试下定义同样功能的函数,按照宏的使用方法时会报错的(不信你试试)

跳回数据库,如果where语句中冗余的那段可以缩略成:
(equal (getf cd field) value)

貌似,就解决了之前的瑕疵,那么试试看:

(defun make-comparison-expr (field value)
(list equal (list getf cd field) value))

似乎有些问题,修缮下:

(defun make-comparison-expr (field value)
(list 'equal (list 'getf 'cd field) value))

多了一些单引号('),意思是,仅随其后的东东不要执行,就当作字符串。试试:

*(make-comparison-expr :rating 10)
(EQUAL (GETF CD :RATING) 10)

结果似乎又是一个Lisp语句,这里就涉及到lisp的终极奥义了:自己生产代码并执行(一如既往的,这是后话了)。
如果你想要实现一段部分实现的代码,那么似乎有些麻烦,lisp也想到了:

'(1 2 (+ 1 2)) ==> (1 2 (+ 1 2))
`(1 2 (+ 1 2)) ==> (1 2 (+ 1 2))
`(1 2 ,(+ 1 2)) ==> (1 2 3)

注意:第一行的那个是单引号('),而下面两行是背引号(`),在键盘位置的左上角ESC按钮下面一个位置,它的右边就是感叹号、1,想要部分实现代码的时候,在实现部分前加上逗号就可以了。
下面把之前的函数变异一下:

(defun make-comparison-expr (field value)
`(equal (getf cd ,field) ,value))

在外层包一层循环体:

(defun make-comparisons-list (fields)
(loop while fields
collecting (make-comparison-expr (pop fields) (pop fields))))

现在,可以将我们的where函数升级成宏:

(defmacro where (&rest clauses)
`#'(lambda (cd) (and ,@(make-comparisons-list clauses))))

先来解释下",@"组合的用处:将后面的执行结果向上并入

`(and ,(list 1 2 3)) ==> (AND (1 2 3))
`(and ,@(list 1 2 3)) ==> (AND 1 2 3)
`(and ,@(list 1 2 3) 4) ==> (AND 1 2 3 4)

而这里的"&rest"与"&key"类似,&key是只接受定义好的":abc"这种东东,而&rest则是接受任意传入并且组成列表list,例如:
(where :title "Give Us a Break" :ripped t)

参数会被组织成这样:
(:title "Give Us a Break" :ripped t)

好不容易写好的东西,应该试下:

* (select (where :title "Give Us a Break" :ripped t))
((:TITLE "Give Us a Break" :ARTIST "Limpopo" :RATING 10 :RIPPED T))

看样子工作的不错,而且代码没有重复了,最重要的是,似乎,这不单单适用于cd/music数据库了,更高层次的抽象大大拓宽了它的适用范围。

这里有给出一些宏和函数的区别:宏的抽象层次要比函数高,使得宏更具有普适性,恰如这里的where。

迷你数据库算告一段落了,当然,作为迷你版,它还有很多欠缺,这自有后话。作者终于承认了他在hello完world后就搞出这么个章节的动机了:hello world实在太小儿科了,都不足以彰显lisp的牛X。这~

(未完待续)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值