Archive for lisp

这个问题是老长时间之前的了,我在repl下边看书边写代码测试一些看书想到的问题,然后把你个内建函数名写错了,sbcl提示我参数不是list,但是群里求救别人都没看出来是函数名写错了,后来我发现是特么的函数名写错。但是我很纳闷,为啥会这么个出错模式。 有人推荐我下了lw,lisp works。然后lisp works提示函数未找到。然后就会轻松知道原因了。 后来妮神给我讲,函数调用是根据名字来确定用哪个函数,而cl标准未规定函数调用的时候,是在参数计算前确定哪个函数还是在后,或者中间。 所以这个问题就导致了,sbcl和lw一样是先执行参数后确定函数的,因为可能参数里边会定义这个函数。(javascript也是这样的)sbcl执行参数发现参数不是list报错了,我测试的那个函数是匹配列表的,可以是cons所以不一定是列表,真是凑巧啊。而lw可能回溯回去 报了函数名不正确的错误。 群里另一个人好像用clisp的实现,是先确定函数然后计算参数的。 当时群里写过一个测试代码,杀叔让我写一个python版本的,我在函数里边用了global,然后python也就替换了。

Continue

妮神教的,没看过标准的我只能猜测,不过开始猜测的还正确了 我在一个包文件里这么定义了一个常量 (defconstant +levels+ (vector :error :warning :info :debug)) load的时候说重复定义,这显然是不正确的,我其他地方都没定义过,显然后边那个值又计算了。我在群里问就是后边这个值是不是又计算了一次。 不过我不知道原因是load了多次导致的 妮神的讲解就是,加载到repl的时候会load多次,然后 就会定义多次,而常量不允许定义多次,cl的实现就会判断定义的时候后边的值是否eql,eql就通过,不eql就报错。而vector虽然是不变长的,但是还不是一个对象,执行两次不eql,所以会报错。 直接用defparameter定义的,包里边自己别修改一般不会有问题吧。

Continue

在妮神帮助下,学习了很多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相关包文件。然后写代码测试。

Continue

今天本来想写点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

(in-package :asdf-user)

(asdf:defsystem duang
  :description "A simple Web Framework."
  :author "0x55aa "
  :license "BSD"
  :version "0.0.1"
  :depends-on (#:cl-async)
  :components ((:file "package")
               (:file "server")))
主要定义包的依赖关系 package.lisp
(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") 都行 这么点东西搞了一晚上

Continue

从昨天到今天,是决定写东西用的语言,准备用一个函数式语言。前几天连想没想用cl找库开搞,不过这个过程是困难的,找到没几个相关库,还感觉很不如意,文档只有简单的介绍,我这刚学会语法的水平简直用不了,对库调用什么的,我直接找了sbcl的io-net包,但是还是放弃了,准备先用cl写点小程序再说。 其实golang写这个东西是个不错的选择,但是对打括号闭合感觉太不有爱,也对golang相对熟悉不想再研究了。今天中午编译了两个 小时的rust,看了一下很不错,要用的东西基本有,现成的没有,如果不考虑花费时间会很长,就会边学边用了。然后相继看了ocaml,haskell,scheme这几个听的多的,比较了一下官网,库,和文档,最后选择了haskell。scheme 语法比较合我意,但是目测和cl应该一样的悲剧。总的来说,从官网内容丰富程度,我选择了scheme。scheme官网上还有很多本scheme的书,很好。

Continue

sbcl的终端交互模式,不支持方向键,终端的快捷键也不支持,输入程序简直但疼的要命,然后想找一个好点的。下午玩玩学学,搞了一下午。 最终敲定的方案:vim+slimv.vim。http://www.vim.org/scripts/script.php?script_id=2531装一个小插件就行了。这个是仿的emacs+slime的。 这个也是一个方案,但是对emacs的快捷键不熟悉,也不想学。话说写lisp代码好像要用emacs。。。觉得还是小小的研究一下 还有一个是rlwrap,开始也是想找一个类似ipython一样的。找到:http://stackoverflow.com/questions/11109249/how-to-customize-the-sbcl-repl rlwrap sbcl就行了。 网速不给力,也没有下载成功。 基本的命令 配置什么的可以找安装的文档。这里有教程:http://kovisoft.bitbucket.org/tutorial.html 还是很好的一个插件!

Continue

今天有想着瞎折腾了,帮老师改毕业设计还早着,但是实在不想写,可能因为感冒不爽。还是对不了解的东西感兴趣,然后开始了。 找到两个库,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.写程序:

(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)

Continue

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,要替换数组的某个元素,我们使用 setfaref。 > (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 具有两个字段 xy,操作方法: [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-xpoint-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函数还真不是一般的多,从第一次看就感觉有很多函数没必要有,现在的感觉是无论用其他的能不能实现,只要你想到的就必须用一个函数可以直接实现。感觉好怪

Continue

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列表操作的函数好多,大概试了,就不记录了,等以后查看文档。

Continue

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"]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嵌套列表"]lisp嵌套列表[/caption] 如果参数是一个 Cons 对象,函数 consp 返回真。

(defun our-listp (x) (or (null x) (consp x)))  因为所有不是 Cons 对象的东西就是一个原子 (atom),判断式 atom 可以这样定义:
(defun our-atom (x) (not (consp x)))
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 等同于取 nthcdrcar。并且都是零索引。 Common Lisp 定义了函数 first 直到 tenth 可以取得列表对应的元素,但不是 零索引。 函数 last 返回列表的最后一个 Cons 对象。此外, Common Lisp 定义了像是 caddr 这样的函数,它是 cdrcdrcar 的缩写 (car of cdr of cdr)。所有这样形式的函数 cxr ,其中 x 是一个字串,最多四个 ad ,在 Common Lisp 里都被定义好了。

Continue