[color=red]注意:[/color]如果你是初学者,甚至都没有其他任何语言基础,那么此篇,即<迷你数据库下>,可以略过,作为前两篇(上、中)的总结、优化,固然很强大,但个人感觉极有可能把初学者带到沟里去,这里涉及到一些较深的东西,并且作者没有给出详解(一如既往的"后面篇章会有的")。so,阅读需谨慎,此篇可跳过~
好吧,如果上面的红字没有能够阻止你的话,就让我们开始。
[b]让你的代码变得更简洁[/b]
虽然之前的数据库也算基本可用了,不过明眼人估计都看出来有很多代码冗余的存在,比如where中的这句:
在功用上,为了能够匹配所有情况而不得不每一次都做多重判定(浪费了效能),在代码上,除了关键字的区别外,根本是一个模子啊,那么为什么不就用同一个模子来做呢。
咱的期望是,如果要如此查找:
那么实现的代码最终看起来便是这个样子:
只判断提供的东西。
其实,用lisp是非常容易达到这个目标的,不过这里就得涉及到lisp中的大杀器"宏"了,之前曾经简单解释成"当成公用函数",事实当然并非如此。宏即是宏,函数即是函数(有点绕)。不过在lisp中使用方式倒是差不多。目前也只需要知其形,至于意么,就见仁见智了~
跳出数据库,在这里简单建个宏,用用看:
宏和函数的定义关键字是不同的。这里reverse是自带宏,作用是:
用下咱写的宏:
结果:
是不是理解这个宏的用法了,此宏将lisp语法逆向了,如果你尝试下定义同样功能的函数,按照宏的使用方法时会报错的(不信你试试)
跳回数据库,如果where语句中冗余的那段可以缩略成:
貌似,就解决了之前的瑕疵,那么试试看:
似乎有些问题,修缮下:
多了一些单引号('),意思是,仅随其后的东东不要执行,就当作字符串。试试:
结果似乎又是一个Lisp语句,这里就涉及到lisp的终极奥义了:自己生产代码并执行(一如既往的,这是后话了)。
如果你想要实现一段部分实现的代码,那么似乎有些麻烦,lisp也想到了:
注意:第一行的那个是单引号('),而下面两行是背引号(`),在键盘位置的左上角ESC按钮下面一个位置,它的右边就是感叹号、1,想要部分实现代码的时候,在实现部分前加上逗号就可以了。
下面把之前的函数变异一下:
在外层包一层循环体:
现在,可以将我们的where函数升级成宏:
先来解释下",@"组合的用处:将后面的执行结果向上并入
而这里的"&rest"与"&key"类似,&key是只接受定义好的":abc"这种东东,而&rest则是接受任意传入并且组成列表list,例如:
参数会被组织成这样:
好不容易写好的东西,应该试下:
看样子工作的不错,而且代码没有重复了,最重要的是,似乎,这不单单适用于cd/music数据库了,更高层次的抽象大大拓宽了它的适用范围。
这里有给出一些宏和函数的区别:宏的抽象层次要比函数高,使得宏更具有普适性,恰如这里的where。
迷你数据库算告一段落了,当然,作为迷你版,它还有很多欠缺,这自有后话。作者终于承认了他在hello完world后就搞出这么个章节的动机了:hello world实在太小儿科了,都不足以彰显lisp的牛X。这~
(未完待续)
好吧,如果上面的红字没有能够阻止你的话,就让我们开始。
[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。这~
(未完待续)