这个问题是老长时间之前的了,我在repl下边看书边写代码测试一些看书想到的问题,然后把你个内建函数名写错了,sbcl提示我参数不是list,但是群里求救别人都没看出来是函数名写错了,后来我发现是特么的函数名写错。但是我很纳闷,为啥会这么个出错模式。
有人推荐我下了lw,lisp works。然后lisp works提示函数未找到。然后就会轻松知道原因了。
后来妮神给我讲,函数调用是根据名字来确定用哪个函数,而cl标准未规定函数调用的时候,是在参数计算前确定哪个函数还是在后,或者中间。
所以这个问题就导致了,sbcl和lw一样是先执行参数后确定函数的,因为可能参数里边会定义这个函数。(javascript也是这样的)sbcl执行参数发现参数不是list报错了,我测试的那个函数是匹配列表的,可以是cons所以不一定是列表,真是凑巧啊。而lw可能回溯回去 报了函数名不正确的错误。
群里另一个人好像用clisp的实现,是先确定函数然后计算参数的。
当时群里写过一个测试代码,杀叔让我写一个python版本的,我在函数里边用了global,然后python也就替换了。
Read more...
Archive for lisp
common lisp 常量定义的本质
妮神教的,没看过标准的我只能猜测,不过开始猜测的还正确了
我在一个包文件里这么定义了一个常量
(defconstant +levels+ (vector :error :warning :info :debug))
load的时候说重复定义,这显然是不正确的,我其他地方都没定义过,显然后边那个值又计算了。我在群里问就是后边这个值是不是又计算了一次。
不过我不知道原因是load了多次导致的
妮神的讲解就是,加载到repl的时候会load多次,然后 就会定义多次,而常量不允许定义多次,cl的实现就会判断定义的时候后边的值是否eql,eql就通过,不eql就报错。而vector虽然是不变长的,但是还不是一个对象,执行两次不eql,所以会报错。
直接用defparameter定义的,包里边自己别修改一般不会有问题吧。
Read more...
common lisp包编译方法
在妮神帮助下,学习了很多cl的知识,后边都总结一下。
本想晚上洗袜子,跑步。写代码写的上火,写了一晚上写了40行cl代码,四个宏,并且功能还没做完。想给框架先写个log模块的,用宏可以编译前判断log打印等级决定生成是否打印,现在整的完全不想写cl代码了[泪流满面] 洗洗睡,袜子明天起的早再洗
今天早上思路清晰多了,代码精简到34行.修复了一个bug。问题是修复了这个bug,展开式变成4行,没修复之前展开式只有一行,不爽 ,,四行的话和写个函数没啥区别了
昨晚上和今天早晨的微博,然后就不记录前后原因了。
说说怎么编译包。
首先要load 依赖包,不然会编译的时候找不到。
(ql:quickload "cl-async")
编译包的顺序,要先编译package.lisp并且load。不然编译别的文件会找不到你自己定义的包.
(load (compile-file "package.lisp"))
之后按顺序编译其他文件
(load (compile-file "logging.lisp"))
(load (compile-file "server.lisp"))
这样其实就可以执行代码了。不过退出repl从新加载的时候,顺序一样,要先load依赖包,然后loadpackage,最后load相关包文件。然后写代码测试。
Read more...
common lisp使用asdf打包程序总结
今天本来想写点web框架代码,早早睡觉的,框架代码写了一点。发现测试时候load 然后执行,想文件多了没法搞了。先看看cl打包吧。
然后就查看过的书,找讲包的内容,发现很少。就一个defpackage,实用cl编程里边讲的多一点,注意事项很多,然并卵,写完发现一样不好用。
去群里求救,需要asdf这么个东西,起了个蛋疼名。之前用过但是不知道干嘛的,文档
https://www.common-lisp.net/project/asdf/asdf.html
首页有个有意思的地方Examples
Download any of the many packages available through Quicklisp to see as many examples.
哈哈,不过文档里有。
看文档sbcl里边好像自带asdf,我装过quicklisp,并且启动repl的时候自动加载。可以直接使用(asdf:asdf-version)看到asdf的版本3.0.2
duang.asd
Read more...
(in-package :asdf-user) (asdf:defsystem duang :description "A simple Web Framework." :author "0x55aa主要定义包的依赖关系 package.lisp" :license "BSD" :version "0.0.1" :depends-on (#:cl-async) :components ((:file "package") (:file "server")))
(defpackage :duang (:use :common-lisp :cl-async) (:nicknames dg) (:export #:start-server))server.lisp
(in-package :duang) (defun start-server () ())然后代码的位置要能被sbcl发现,看文档可以自定义,等会儿看看quicklisp文档有没有。我直接放到~/quicklisp/local-projects/文件夹下了。 最后在repl里(asdf:load-system "duang") (dg:start-server)就行了 (require :duang) (load ) (ql:quickload "duang") 都行 这么点东西搞了一晚上
函数语言的纠结
从昨天到今天,是决定写东西用的语言,准备用一个函数式语言。前几天连想没想用cl找库开搞,不过这个过程是困难的,找到没几个相关库,还感觉很不如意,文档只有简单的介绍,我这刚学会语法的水平简直用不了,对库调用什么的,我直接找了sbcl的io-net包,但是还是放弃了,准备先用cl写点小程序再说。
其实golang写这个东西是个不错的选择,但是对打括号闭合感觉太不有爱,也对golang相对熟悉不想再研究了。今天中午编译了两个 小时的rust,看了一下很不错,要用的东西基本有,现成的没有,如果不考虑花费时间会很长,就会边学边用了。然后相继看了ocaml,haskell,scheme这几个听的多的,比较了一下官网,库,和文档,最后选择了haskell。scheme 语法比较合我意,但是目测和cl应该一样的悲剧。总的来说,从官网内容丰富程度,我选择了scheme。scheme官网上还有很多本scheme的书,很好。
Read more...
sbcl的repl折腾一个比较完美的实现
sbcl的终端交互模式,不支持方向键,终端的快捷键也不支持,输入程序简直但疼的要命,然后想找一个好点的。下午玩玩学学,搞了一下午。
最终敲定的方案:vim+slimv.vim。http://www.vim.org/scripts/script.php?script_id=2531装一个小插件就行了。这个是仿的emacs+slime的。
这个也是一个方案,但是对emacs的快捷键不熟悉,也不想学。话说写lisp代码好像要用emacs。。。觉得还是小小的研究一下
还有一个是
Read more...
rlwrap,开始也是想找一个类似ipython一样的。找到:
http://stackoverflow.com/questions/11109249/how-to-customize-the-sbcl-repl
rlwrap sbcl就行了。
网速不给力,也没有下载成功。
基本的命令 配置什么的可以找安装的文档。这里有教程:http://kovisoft.bitbucket.org/tutorial.html
还是很好的一个插件!
ubuntu下安装sbcl,进行网站开发
今天有想着瞎折腾了,帮老师改毕业设计还早着,但是实在不想写,可能因为感冒不爽。还是对不了解的东西感兴趣,然后开始了。
找到两个库,cl-http和hunchentoot,一个重量级,一个轻量级。选择后者了,因为只是想玩玩,写的代码肯定helloworld水平,just do it。
主要参考文章:http://blog.csdn.net/cx1468059916/article/details/8262515很详细
1.安装sbcl,执行命令:sudo apt-get install sbcl,sbcl好像是开源里边最受欢迎的。
2.安装quicklisp,是进行库管理的,方便安装。
下载quicklisp。wget http://beta.quicklisp.org/quicklisp.lisp
进入sbcl,载入:* (load "quicklisp.lisp")
安装(quicklisp-quickstart:install)
每次启动sbcl,载入sbcl,(ql:add-to-init-file)
安装库:(ql:quickload "system-name")
在命令执行过程中会有提示。
3.安装hunchentoot:* (ql:quickload "hunchentoot")其中安装了好多依赖库,
安装html-template:* (ql:quickload "html-template")
4.写程序:
Read more...
(asdf:oos 'asdf:load-op :hunchentoot)
(asdf:oos 'asdf:load-op :html-template)
(defun myserver ()
(hunchentoot:start (make-instance 'hunchentoot:easy-acceptor :port 8080))
(hunchentoot:define-easy-handler (greet :uri "/hello") ()
(setf (hunchentoot:content-type*) "text/html; charset=utf-8")
(with-output-to-string (stream)
(html-template:fill-and-print-template
#p"hello_world.html"
()
:stream stream))))
5.运行:
* (load "hello_world.lisp")
* (myserver)
common lisp数组(Array)-字符字符串
1.数组,Array
make-array 构造一个数组。
[9]> (make-array '(2 3))
#2A((NIL NIL NIL) (NIL NIL NIL))
[10]> (make-array '(3 3))
#2A((NIL NIL NIL) (NIL NIL NIL) (NIL NIL NIL))
其中 n 是数组的维度。
[11]> #2a((1 2 3) (4 4 4))
#2A((1 2 3) (4 4 4))
可以使用:initial-element 参数初始化数组元素的值。
[12]> (make-array '(2 3) :initial-element 2)
#2A((2 2 2) (2 2 2))
取出数组内的元素我们调用 aref,要替换数组的某个元素,我们使用 setf 和 aref。
> (setf arr (make-array '(2 3)))
#2A((NIL NIL NIL) (NIL NIL NIL))
> (aref arr 0 1)
NIL
> (setf (aref arr 0 1) 'b)
B
> (aref arr 0 1)
B
> arr
#2A((NIL B NIL) (NIL NIL NIL))
2.字符串
一个字符 c 用 #\c 表示
字符比较函数 char< (小于), char<= (小于等于), char= (等于), char>= (大于等于) , char> (大于),以及 char/= (不同)
aref 来取出元素,但对一个字串,你可以使用更快的 char 函数。
[30]> (aref "abc" 1)
#\b
[31]> (char "abc" 1)
#\b
比较两个字串,你可以使用通用的 equal 函数,但还有一个忽略大小写的比较函数 string-equal。
连接字符串
[7]> (concatenate 'string "not " "to worry")
"not to worry"
3.结构
定义结构使用 defstruct,
[8]> (defstruct point x y)
POINT
定义了一个 point 具有两个字段 x 与 y,操作方法:
[9]> (setf p (make-point :x 0 :y 0))
#S(POINT :X 0 :Y 0)
[10]> (point-x p)
0
[11]> (point-y p)
0
[12]> (setf (point-y p) 2)
2
[13]> p
#S(POINT :X 0 :Y 2)
测试是否是一个point:
[14]> (point-p p)
T
make-point , point-p , copy-point , point-x 及 point-y 函数都是在定义结构时隐式定义了。
4.哈希表 (Hash Table)
当列表的长度大幅上升时(或是 10 个元素),使用哈希表会来得比较快。
[1]> (setf ht (make-hash-table))
#S(HASH-TABLE :TEST FASTHASH-EQL)
[2]> (gethash 'color ht)
NIL ;
NIL
[3]> (setf (gethash 'color ht) 'red)
RED
[4]> (gethash 'color ht)
RED ;
T
[5]> ht
#S(HASH-TABLE :TEST FASTHASH-EQL (COLOR . RED))
[6]> (setf (gethash 'size ht) '10)
10
[7]> ht
#S(HASH-TABLE :TEST FASTHASH-EQL (SIZE . 10) (COLOR . RED))
[12]> (remhash 'size ht)
NIL
[13]> ht
#S(HASH-TABLE :TEST FASTHASH-EQL (COLOR . RED))
make-hash-table构造一个哈希表,gethash第一个参数是键值,第二个是哈希表。返回的第一个参数是键值对应的值,不存在返回nil。第二个参数表示是否存在对应的值,不存在为nil,应该主要为了区别方便判断,比如nil可能为值。remhash是删除一个记录。
[14]> (setf ht2 (make-hash-table :size 5))
#S(HASH-TABLE :TEST FASTHASH-EQL)
[15]> (setf ht3 (make-hash-table :test #'equal))
#S(HASH-TABLE :TEST FASTHASH-EQUAL)
参数:size指定哈希表的大小,:test指定查询时候比较时用的函数,从构造完返回的值有显示:test的不同。
common lisp函数还真不是一般的多,从第一次看就感觉有很多函数没必要有,现在的感觉是无论用其他的能不能实现,只要你想到的就必须用一个函数可以直接实现。感觉好怪
Read more...
common lisp列表学习2
1.映射函数
[1]> (setf l '(1 2 3))
(1 2 3)
[8]> (mapcar #'(lambda (x) (+ x 10)) l)
(11 12 13)
maplist 接受同样的参数,将列表的渐进的下一个 cdr 传入函数
[9]> (maplist #'(lambda (x) x) l)
((1 2 3) (2 3) (3))
2.树
看完这一节,才对lisp的其中一个优点有所理解。就像lisp的名字“Lisp” 起初是 “LISt Processor” 的缩写。都是列表啊。
Cons 对象可以想成是二元树, car 代表右子树,而 cdr 代表左子树。图就不传了,原图有点错误,这个比较好理解。
我们有下面的列表。
[13]> (setf x 1)
1
[15]> (and (integerp x) (zerop (mod x 2)))
NIL
[16]> (setf x 2)
2
[17]> (and (integerp x) (zerop (mod x 2)))
T
[18]> (setf x 2.0)
2.0
[19]> (and (integerp x) (zerop (mod x 2)))
NIL
用来判断x是整数,并且被2 整除,这不是函数形式,想把其中的x改成y的时候。我们将他看成整个列表进行操作。看到这里我才明白,汗。
[20]> (subst 'y 'x '(and (integerp x) (zerop (mod x 2))))
(AND (INTEGERP Y) (ZEROP (MOD Y 2)))
单引号的功能前边有介绍,但是没这样的例子,当时还是仅仅的理解为'(a b c)。
[24]> (cons 'a 'b)
(A . B)
这是一个非正规列表,称之为
[caption id="attachment_808" align="alignnone" width="96" caption="点状列表"][/caption]
。
common lisp列表操作的函数好多,大概试了,就不记录了,等以后查看文档。
Read more...
common lisp的列表(Lists),等式 (Equality)
1.列表(Lists)
cons把两个对象结合成一个有两部分的对象,称之为 Cons 对象。概念上来说,一个 Cons 是一对指针; 第一个是 car ,第二个是 cdr 。
任何非空的列表,都可以被视为一对由列表第一个元素及列表其余元素所组成的列表。
[1]> (setf x (list 'a 'b 'c))
(A B C)
[2]> (car x)
A
[3]> (cdr x)
(B C)
看图理解cons。
[caption id="attachment_801" align="alignnone" width="298" caption="lisp list"][/caption]
可以这么些:
[8]> (setf y (cons 'a (cons 'b (cons 'c nil))))
(A B C)
与图中所画内容相对应了
嵌套列表
[9]> (setf z (list 'a (list 'b 'c) 'd))
(A (B C) D)
[10]> (car (cdr z))
(B C)
[caption id="attachment_802" align="alignnone" width="300" caption="lisp嵌套列表"][/caption]
如果参数是一个 Cons 对象,函数 consp 返回真。
Read more...
(defun our-listp (x)
(or (null x) (consp x)))
因为所有不是 Cons 对象的东西就是一个原子 (atom),判断式 atom 可以这样定义:
NIL 是一个原子,同时也是一个列表。
(null x)就是判断x为nil时,返回t。当然空列表也是nil。
2.等式 (Equality)
每一次你调用 cons 时, Lisp 会配置一块新的内存给两个指针。所以如果我们用同样的参数调用 cons 两次,我们得到两个数值看起来一样,但实际上是两个不同的对象:
[14]> (eql (cons 'a nil) (cons 'a nil))
NIL
本质上 equal 若它的参数打印出的值相同时,返回真:
[15]> (equal (cons 'a nil) (cons 'a nil))
T
3.lisp的指针
书上给了一个例子:
[22]> (setf x '(a b c))
(A B C)
[23]> x
(A B C)
[24]> (setf y x)
(A B C)
[25]> y
(A B C)
[26]> (eql x y)
T
当我们给 y 赋一个相同的值时, Lisp 复制的是指针,而不是列表。当你赋一个值给变量或将这个值存在数据结构中,其实被储存的是指向这个值的指针。当你要取得变量的值,或是存在数据结构中的内容时, Lisp 返回指向这个值的指针。
函数 copy-list 接受一个列表,然后返回此列表的复本。
[27]> (setf y (copy-list x))
(A B C)
[28]> (eql x y)
NIL
4.列表的存取
[29]> (nth 0 '(a b c ))
A
[30]> (nth 1 '(a b c ))
B
[35]> (nthcdr 0 '(a b c d))
(A B C D)
[36]> (nthcdr 1 '(a b c d))
(B C D)
[37]> (nthcdr 2 '(a b c d))
(C D)
取第几个元素用nth,nthcdr是取第n个cdr,nth 等同于取 nthcdr 的 car。并且都是零索引。
Common Lisp 定义了函数 first 直到 tenth 可以取得列表对应的元素,但不是 零索引。
函数 last 返回列表的最后一个 Cons 对象。此外, Common Lisp 定义了像是 caddr 这样的函数,它是 cdr 的 cdr 的 car 的缩写 (car of cdr of cdr)。所有这样形式的函数 cxr ,其中 x 是一个字串,最多四个 a 或 d ,在 Common Lisp 里都被定义好了。
(defun our-atom (x) (not (consp x)))