好家伙,这还是本人第一次碰到入门书籍刚hello完world,就讲到数据库这么复杂的东东,而且注意,这里不是怎么连数据库,而是怎样来制作一个数据库,OMG~
或许作者也自知有些夸张(真的么?),本篇开始就申明:此章代码并不求甚解,很多东东自有后面的章节给出详解,这里只是通过具体的应用来加深对lisp的理解和感觉,当然如果学有余力深解下也无不可。
好吧,话不多说,本章的主体案例为:完成一个具有收录、评分功能的cd/music工具(另一种常见的经典案例是“通讯录”,话说在python的简明教程中,能做到这一步就恭喜成为一个pythoner了...)
根据功能,最基本的实现是要有个列表:
*(list 1 2 3)
(1 2 3)
嘿嘿,天然支持啊,那么如果要带上查找功能至少得有key吧:
*(list :a 1 :b 2 :c 3)
(:A 1 :B 2 :C 3)
好了,以上便是lisp式字典了,简单k-v下:
*(getf (list :a 1 :b 2 :c 3) :a)
1
基于这种字典,第一个版本基本就成形了:
*(defun make-cd (title artist rating ripped)
(list :title title :artist artist :rating rating :ripped ripped))
试试:
* (make-cd "Roses" "Kathy Mattea" 7 t)
(:TITLE "Roses" :ARTIST "Kathy Mattea" :RATING 7 :RIPPED T)
数据格式已经确定了下来,下面就是要存储数据了:
*(defvar *db* nil) #defvar为Lisp的内置宏(暂时理解成默认方法好了)
*DB* #输出结果。db是定义的变量,加*号包夹则为全局变量
添加cd工具:
*(defun add-record (cd) (push cd *db*)) #这里的push也是默认宏,用作添加
试试:
*(add-record (make-cd "Roses" "Kathy Mattea" 7 t))
((:TITLE "Roses" :ARTIST "Kathy Mattea" :RATING 7 :RIPPED T))
*(add-record (make-cd "Fly" "Dixie Chicks" 8 t))
((:TITLE "Fly" :ARTIST "Dixie Chicks" :RATING 8 :RIPPED T)
(:TITLE "Roses" :ARTIST "Kathy Mattea" :RATING 7 :RIPPED T))
*(add-record (make-cd "Home" "Dixie Chicks" 9 t))
((:TITLE "Home" :ARTIST "Dixie Chicks" :RATING 9 :RIPPED T)
(:TITLE "Fly" :ARTIST "Dixie Chicks" :RATING 8 :RIPPED T)
(:TITLE "Roses" :ARTIST "Kathy Mattea" :RATING 7 :RIPPED T))
看起来还不错,添加功能算是实现了,看看我们的DB数据:
**db*
((:TITLE "Home" :ARTIST "Dixie Chicks" :RATING 9 :RIPPED T)
(:TITLE "Fly" :ARTIST "Dixie Chicks" :RATING 8 :RIPPED T)
(:TITLE "Roses" :ARTIST "Kathy Mattea" :RATING 7 :RIPPED T))
似乎有点别扭,如果可以变成这样,或许会好些:
TITLE: Home
ARTIST: Dixie Chicks
RATING: 9
RIPPED: T
TITLE: Fly
ARTIST: Dixie Chicks
RATING: 8
RIPPED: T
TITLE: Roses
ARTIST: Kathy Mattea
RATING: 7
RIPPED: T
那么,咱标准化下输出:
*(defun dump-db ()
(dolist (cd *db*)
(format t "~{~a:~10t~a~%~}~%" cd)))
是不是有点晕了,这里的"~"据说很神奇,变化万千(与悟空有的一拼),这里简单解释下此处的几种变化:
~a: 根据数据格式美化输出,对普通字符串无效,对key做大写转换
~t: 制表符,~10t表示隔10位
~{、~}:成对出现,常用于循环体中,用来包夹循环体,可以嵌套
~%: 换行符
这里的dolist也是默认宏,用来循环、遍历list类数据,简化输出用,如果不用的话,代码会变成这个样子:
*(defun dump-db ()
(format t "~{~{~a:~10t~a~%~}~%~}" *db*))
如何取舍,自个儿决定~
输出基本ok了,那么输入呢,加点交互是个不错的主意:
*(defun prompt-read (prompt)
(format *query-io* "~a: " prompt) #query-io被申明为全局变量了
(force-output *query-io*) #force-output是宏,等待输入
(read-line *query-io*)) #read-line是宏,读取输入
组合下:
*(defun prompt-for-cd ()
(make-cd
(prompt-read "Title")
(prompt-read "Artist")
(prompt-read "Rating")
(prompt-read "Ripped [y/n]")))
理论上,有些字段是有必要验证下的:
*(parse-integer (prompt-read "Rating")) #验证是否整形
当然,不是就抛错也不太合理,那么放松下,不是就给个默认值好了:
*(parse-integer (prompt-read "Rating") :junk-allowed t)
虽然不报错,不过NIL似乎也不太好。再精确些:
*(or (parse-integer (prompt-read "Rating") :junk-allowed t) 0) #出错就给0
在lisp中也有专门的"yes/no"宏:
*(y-or-n-p "Ripped [y/n]: ")
于是,上面的代码变成了这个样子:
*(defun prompt-for-cd ()
(make-cd
(prompt-read "Title")
(prompt-read "Artist")
(or (parse-integer (prompt-read "Rating") :junk-allowed t) 0)
(y-or-n-p "Ripped [y/n]: ")))
如过要添加一堆cd,再包装一层循环就更靠谱了:
*(defun add-cds ()
(loop (add-record (prompt-for-cd))
(if (not (y-or-n-p "Another? [y/n]: ")) (return))))
试试看:
*(add-cds)
Title: Rockin' the Suburbs
Artist: Ben Folds
Rating: 6
Ripped [y/n]: y
Another? [y/n]: y
Title: Give Us a Break
Artist: Limpopo
Rating: 10
Ripped [y/n]: y
Another? [y/n]: y
Title: Lyle Lovett
Artist: Lyle Lovett
Rating: 9
Ripped [y/n]: y
Another? [y/n]: n
NIL
恩,不错哦~
作为一个靠谱的工具,至少的,数据得要持久存储吧:
*(defun save-db (filename)
(with-open-file (out filename
:direction :output
:if-exists :supersede)
(with-standard-io-syntax
(print *db* out))))
with-open-file宏很好理解了,打开文件写入,若存在就替换。with-standard-io-syntax宏主要目的是保持写入和即将的读出格式一致,也可以简单理解成序列化(简单理解不算误人子弟吧~)
有存就有取:
*(defun load-db (filename)
(with-open-file (in filename)
(with-standard-io-syntax
(setf *db* (read in)))))
这里with-open-file参数好像少了些,默认就是读么,也不存在覆盖的问题。
还有这里的SETF和上面的GETF也算是一对冤家了:一放一取。
(未完待续)
分享到:
相关推荐
综上所述,本书《Practical Common Lisp-1st-2005》不仅是一本向读者介绍Common Lisp编程语言的书籍,更是一本能够帮助读者深入理解语言精髓,并将其应用于解决实际问题的实用指南。它适合那些希望通过编程语言来...
common-lisp-the-language-second-edition.PDF
cad-lisp-3-表操作.LSP.lsp
《Practical Common Lisp笔记》是一本深入探讨Common Lisp编程语言的实用教程。Common Lisp是一种功能强大的多范式编程语言,以其动态类型、宏系统和丰富的内置数据结构而闻名。这篇笔记详细记录了作者在学习过程中...
《Common Lisp the Language, 2nd Edition》是Guy L. Steele所著的一本关于Common Lisp编程语言的权威指南,作为第二版,它不仅继承了前一版的经典,还对Lisp语言进行了深入的扩展和更新。这本书是Lisp爱好者的宝贵...
标题中的"emacs-lisp-intro-2.04.tar.gz"是一个典型的压缩文件名,它表明这个文件是一个关于Emacs Lisp的介绍性资料,并且版本号是2.04,格式为tar.gz。tar.gz是一种在Linux和Unix系统中常用的文件压缩格式,它先用...
标题:“实用Common.Lisp编程.pdf” 描述:“实用Common.Lisp编程.pdf,2011.10出版” 从这些信息中,我们可以提炼出几个关键的知识点: ### Common Lisp语言简介 Common Lisp是一种高级的、通用的、多范式的编程...
在压缩文件中,只有一个名为"practical_common_lisp.pdf"的文件,这是本书的PDF版本。由于原版PDF文件较大,经过7z压缩后,文件大小显著减小,便于下载和存储,但同时也能保持较好的图像和文字质量,满足读者的阅读...
Provides practical advice for the construction of Common Lisp programs. Shows examples of how Common Lisp is best used. Illustrates and compares features of the most popular Common Lisp systems on ...
AutoLisp源文件--标注高程.LSP
ANSI Common Lisp 中文翻译版.pdf 此资源是 ANSI Common Lisp 的中文翻译版,涵盖了 Common Lisp 语言的基础知识和高级主题。该资源包含了 17 章节,从基础的列表、特殊数据结构、控制流程、函数、输入与输出、符号...
1. **数据库开发**:Common Lisp可以通过多种方式与数据库交互,如使用SQL或特定的Lisp库来操作数据。 2. **单元测试框架**:书中介绍了一种用于编写测试用例的方法,并且展示了如何使用Common Lisp构建一个完整的...
这本《Practical Common Lisp》之所以号称Practical,正是因为这本书大量介绍Common Lisp在现实世界中的各种应用方式,算是第一本「入世传教」的Common Lisp著作。《Practical Common Lisp》是目前最畅销的Common ...
CommonLisp 的简单而复杂的MP3 数据库,根据实用CommonLisp编程
Practical Common Lisp 学习lisp的入门书籍
计算多个数字之和、计算多条线段长度之和、插入墙高标注、查询多段线顶点坐标并绘制、自动生成页码、绘制示坡线、插入排水箭头 https://blog.csdn.net/qq_24141055/article/details/121446354
If you’re interested in Lisp as it relates to Python or Perl, and want to learn through doing rather than watching, Practical Common Lisp is an excellent entry point. — Chris McAvoy, Chicago Python ...
标题中的“Lisp-music-player.rar”表明这是一个基于Lisp语言开发的音乐播放器软件,其源代码或可执行文件被压缩在RAR格式的文件中。RAR是一种流行的压缩格式,通常用于存储和分发多个文件,它允许用户将多个文件...
Common-Lisp-Actors, 通用Lisp的actor系统 这是一个简单且易于使用的Actor系统,在。设置需要波尔多螺纹。http://common-lisp.net/project/bordeaux-threads/ 2. 加载 actors.lisp 并开始使用它。 如果你有 Quick