(defparameter *foo* 6) 全局变量,可修改
(defvar *foo* 5) 全局变量,只在定义时赋值一次
(let (a 5)) 词法变量
defun 定义全局函数
flet 定义局部函数,函数间不可相互引用
labels 定义局部函数,函数间可相互引用
'(expt 2 3) 数据模式
(expt 2 3) 代码模式
列表连接
> (cons 'pork (cons 'beef (cons 'chicken ())))
(PORK BEEF CHICKEN)
首个元素
> (car '(pork beef chicken))
PORK
除掉首个元素之后的列表
> (cdr '(pork beef chicken))
(BEEF CHICKEN)
除掉首个元素之后的列表,之后再取首个元素 =》 第二个元素嘛。。。
> (cadr '(pork beef chicken))
BEEF
还有 cddr, caddr, cddar, cadadr 等等
按顺序执行语句,最后一条语句的运行结果作为返回值
> (progn (setf *number-was-odd* t)
'odd-number)
when / unless 与progn类似,不过多一个判断条件
cond 条件可以写的比较复杂,case一般用于字符串比对
if 的结构相对简单,一个判定条件,2个分支
> (if (member 1 '(3 4 1 5))
'one-is-in-the-list
'one-is-not-in-the-list)
检查1个元素是否在列表里,如果存在,则返回以该元素为起始的剩余元素列表
> (member 1 '(3 4 1 5))
(1 5)
指定函数,对列表做grep
> (find-if #'oddp '(2 4 5 6))
eq 做符号比较
equal 做多种类型数据比较
eql 做数值和符号比较
equalp 比较复杂的比较
chap 5 - chap 6
mapcar相当于列表做函数映射(perl中的map)
apply的第一个参数指定调用函数,后面是调用该函数时传入的参数列表
>(defun describe-paths (location edges)
(apply #'append (mapcar #'describe-path (cdr (assoc location edges)))))
根据指定的key找出符合条件的记录
> (find 'y '((5 x) (3 y) (7 z)) :key #'cadr)
(3 Y)
print 每个元素单独打一行
prin1 不加换行符打印
princ 打印字符
read 输入
#\newline #\tab #\space 换行符、tab符、空格符
转换字符串列表
>(defun tweak-text (lst caps lit)
(when lst
(let ((item (car lst))
(rest (cdr lst)))
(cond ((eql item #\space) (cons item (tweak-text rest caps lit)))
((member item '(#\! #\? #\.)) (cons item (tweak-text rest t lit)))
((eql item #\") (cons item (tweak-text rest caps (not lit))))
(lit (cons item (tweak-text rest nil lit)))
(caps (cons (char-upcase item) (tweak-text rest nil lit)))
(t (cons (char-downcase item) (tweak-text rest nil nil)))))))
> (tweak-text '(#\a #\Space #\b) 't 'nil)
(tweak-text '(#\a #\Space #\b) 't 'nil)
总共6个可能:
- 如果是空格,原样输出
- 如果是!/?/. 等结束符,下个字符就转成大写(tweak-text rest t lit)
- 如果是"子字符串,则转换之前的lit模式
- 如果正处于lit模式,则不强制转换成大写 (tweak-text rest nil lit)
- 如果caps为真,则强制转换为大写,后面默认不转换(cons (char-upcase item) (tweak-text rest nil lit))
- 否则,转换为小写,后面默认不转换 (cons (char-downcase item) (tweak-text rest nil nil))
> (defun game-print (lst)
(princ (coerce (tweak-text (coerce (string-trim "() "
(prin1-to-string lst))
'list)
t
nil)
'string))
(fresh-line))
> (game-print '(i am a. test "c aNnd d" b))
I am a. Test "c aNnd d" b
注意game-print的处理过程:
lambda函数
> (mapcar (lambda (n) (/ n 2)) '(2 4 6))
(1 2 3)
chap 7
循环列表,将foo尾部指针指向foo首部
> (defparameter foo '(1 2 3))把exp中非字母数字类的字符都转换成_
FOO
> (setf (cdddr foo) foo)
#1=(1 2 3 . #1#)
(defun dot-name (exp)
(substitute-if #\_ (complement #'alphanumericp) (prin1-to-string exp)))
截取字符串s的前 *max-label-length* -3 个字符,后面加3个点
(concatenate 'string (subseq s 0 (- *max-label-length* 3)) "...")
一张edges图中有多个node,每个node有多个edge,按结构嵌套处理
(defun edges->dot (edges)调用thunk函数,输出的结果写入fname文件,再执行指定shell命令画图
(mapc (lambda (node)
(mapc (lambda (edge)
(fresh-line)
(princ (dot-name (car node)))
(princ "->")
(princ (dot-name (car edge)))
(princ "[label=\"")
(princ (dot-label (cdr edge)))
(princ "\"];"))
(cdr node)))
edges))
(defun dot->png (fname thunk)
(with-open-file (*standard-output*
fname
:direction :output :if-exists :supersede)
(funcall thunk))
(ext:shell (concatenate 'string "dot -Tpng -O " fname)))
maplist对列表的cdr依次调用指定的函数
(defparameter *wizard-edges* '((living-room (garden west door)
(attic upstairs ladder))
(garden (living-room east door))
(attic (living-room downstairs ladder))))
(defun uedges->dot (edges)
(maplist (lambda (lst)
(mapc (lambda (edge)
(unless (assoc (car edge) (cdr lst))
(fresh-line)
(princ (dot-name (caar lst)))
(princ "--")
(princ (dot-name (car edge)))
(princ "[label=\"")
(princ (dot-label (cdr edge)))
(princ "\"];")))
(cdar lst)))
edges))
用maplist遍历edges子集合,注意每次遍历都是取cdr
每次遍历,先用(cdar lst)取出此次首个对象对应的边的集合
以首次遍历的节点living-room为例,就有两条边(garden west door)、(attic upstairs ladder)
对于每一条边,检查这条边的节点(car edge) = garden、attic,是否会在后面(cdr lst) =(garden, attic)遇到
如果后面还会遇到,就先不打印,死撑到最后。。。
通过maplist、assoc的判断,实现去重,很漂亮!
chap 8
循环
(loop repeat 10
collect 1)
(loop for n from 1 to 10
collect n)
取出所有从node能够到达的节点集合visited
取出node起始的边:(direct-edges node edge-list)
从node指向的节点开始递归遍历:(traverse (cdr edge))
(defun get-connected (node edge-list)
(let ((visited nil))
(labels ((traverse (node)
(unless (member node visited)
(push node visited)
(mapc (lambda (edge)
(traverse (cdr edge)))
(direct-edges node edge-list)))))
(traverse node))
visited))
分割出不相邻的节点集:
从nodes取出一个节点node,然后调用get-connected遍历,得到该节点node能够到达的节点集合connected
对剩余unconnected的节点集合重复上述处理过程
(defun find-islands (nodes edge-list)
(let ((islands nil))
(labels ((find-island (nodes)
(let* ((connected (get-connected (car nodes) edge-list))
(unconnected (set-difference nodes connected)))
(push connected islands)
(when unconnected
(find-island unconnected)))))
(find-island nodes))
islands))
按顶点置换边集合:
把(1 2) (1 3) 换成(1 (2) (3))
(remove-duplicates (mapcar #'car edge-list)) 取出不重复的顶点集
对每个顶点node1,取出对应的边集合(remove-duplicates (direct-edges node1 edge-list)
把每个边集合的顶点换成list形式 (list (cdr edge))
(defun edges-to-alist (edge-list)
(mapcar (lambda (node1)
(cons node1
(mapcar (lambda (edge)
(list (cdr edge)))
(remove-duplicates (direct-edges node1 edge-list)
:test #'equal))))
(remove-duplicates (mapcar #'car edge-list))))
添加cops标记
对于每个顶点的alist集合做处理:(1 (2) (3))
例如node1 = 1 , node1-edges = (2) (3)
如果nod1-edges里的顶点node2,与node1连接的边正好在edges-with-cops边集合里
则把(node2) 转换成(node2 'cops),否则,原样输出edge
(defun add-cops (edge-alist edges-with-cops)
(mapcar (lambda (x)
(let ((node1 (car x))
(node1-edges (cdr x)))
(cons node1
` (mapcar (lambda (edge)
(let ((node2 (car edge)))
(if (intersection (edge-pair node1 node2)
edges-with-cops
:test #'equal)
(list node2 'cops)
edge)))
node1-edges))))
edge-alist))
检查是否至少有一个cops
(some #'cdr (cdr (assoc n edge-alist)))mapcan 将map之后的结果都合并成一个列表
> (defun ingredients (order)
(mapcan (lambda (burger)
(case burger
(single '(patty))
(double '(patty patty))
(double-cheese '(patty patty cheese))))
order))
INGREDIENTS
> (ingredients '(single double-cheese double))
'(PATTY PATTY PATTY CHEESE PATTY PATTY)
chap 9
这章草草看过,回头有空重读一下。。。说实话,这个作者真话痨。。。
数组赋值
> (defparameter x (make-array 3))
#(NIL NIL NIL)
> (setf (aref x 1) 'foo)
FOO
> x
#(NIL FOO NIL)
> (aref x 1)
FOO
列表赋值
> (setf foo '(a b c))
(A B C)
> (second foo)
B
> (setf (second foo) 'z)
Z
> foo
(A Z C)
HASH赋值
> foo
#(NIL NIL (X Y Z) NIL)
> (setf (car (aref foo 2)) (make-hash-table))
#S(HASH-TABLE)
> (setf (gethash 'zoink (car (aref foo 2))) 5)
5
> foo
#(NIL NIL (#S(HASH-TABLE (ZOINK . 5)) Y Z) NIL)
aref在数据量较大的时候比nth快得多,nth仅适用于小数据量
> (nth 1 '(foo bar baz))
BAR
用values关键字让函数返回多个值
> (defun foo ()
(values 3 7))
FOO
> (foo)
3 ;
7
默认只取第1个返回值,也可用multiple-value-bind绑定多个返回值
> (+ (foo) 5)
8
> (multiple-value-bind (a b) (foo)
(* a b))
21
hash版本的edges
(defun hash-edges (edge-list)
(let ((tab (make-hash-table)))
(mapc (lambda (x)
(let ((node (car x)))
(push (cdr x) (gethash node tab))))
edge-list)
tab))
定义结构体
> (defstruct person
name
age
waist-size
favorite-color)
PERSON
新增一个对象
> (defparameter *bob* (make-person :name "Bob"
:age 35
:waist-size 32
:favorite-color "blue"))
*BOB*
对象属性取值、赋值
> (person-age *bob*)
35
> (setf (person-age *bob*) 36)
36
直接批量赋值
> (defparameter *that-guy* #S(person :name "Bob" :age 35 :waist-size 32 :favorite-color "blue"))
一些序列处理函数,find-if 过滤、count 计数、position 首次出现的位置、some 至少存在一个元素满足断言、every 每个元素都满足断言
> (find-if #'numberp '(a b 5 d))求和
5
> (count #\s "mississippi")
4
> (position #\4 "2kewl4skewl")
5
> (some #'numberp '(a b 5 d))
T
> (every #'numberp '(a b 5 d))
NIL
> (reduce #'+ '(3 4 6 5 2))
20
取出最大的偶数
> (reduce (lambda (best item)
(if (and (evenp item) (> item best))
item
best))
'(7 4 6 5 2)
:initial-value 0)
6
初始化数组
(make-array 5 :initial-contents '(1 2 3 4 5))
以list形式返回map结果
> (map 'list
(lambda (x)
(if (eq x #\s)
#\S
x))
"this is a string")
(#\t #\h #\i #\S #\ #\i #\S #\ #\a #\ #\S #\t #\r #\i #\n #\g)
子字符串
> (subseq "america" 2 6)排序
"eric"
> (sort '(5 8 2 4 9 3 6) #'<)
(2 3 4 5 6 8 9)
接口固定,根据参数类型处理(适用于简单判断)
> (defun add (a b)
(cond ((and (numberp a) (numberp b)) (+ a b))
((and (listp a) (listp b)) (append a b))))
ADD
> (add 3 4)
7
> (add '(a b) '(c d))
(A B C D)
接口固定,支持多种参数类型处理(扩展性好)
> (defmethod add ((a number) (b number))
(+ a b))
ADD
> (defmethod add ((a list) (b list))
(append a b))
ADD
chap 10
循环例子
chap 11
输出内容到 t 终端 / nil字符串 / stream
> (format t "Add onion rings for only ~$ dollars more!" 1.5)~s会保留",而~a会去掉分隔符
Add onion rings for only 1.50 dollars more!
NIL
> (format t "I am printing ~s in the middle of this sentence." "foo")
I am printing "foo" in the middle of this sentence.
> (format t "I am printing ~a in the middle of this sentence." "foo")
I am printing foo in the middle of this sentence.
> (format t "I am printing ~10a within ten spaces of room." "foo")
I am printing foo within ten spaces of room.
> (format t "I am printing ~10@a within ten spaces of room." "foo")
I am printing foo within ten spaces of room.
打印数组
> (format t "~{I see a ~a... or was it a ~a?~%~}" *animals*)
两种换行
> (progn (format t "this is on one line ~%")此外还有数值处理,居中、两端对齐等等,用的时候再翻书
(format t "~%this is on another line"))
this is on one line
this is on another line
> (progn (format t "this is on one line ~&")
(format t "~&this is on another line"))
this is on one line
this is on another line
chap 12
写文件
> (with-open-file (my-stream "data.txt" :direction :output)
(print "my data" my-stream))
读文件
> (with-open-file (my-stream "data.txt" :direction :input)
(read my-stream))
"my data"
写字符串
> (defparameter foo (make-string-output-stream))
> (princ "This will go into foo. " foo)
> (princ "This will also go into foo. " foo)
> (get-output-stream-string foo)
"This will go into foo. This will also go into foo. "
socket的简单例子
> (defparameter my-socket (socket-server 4321)) ;ON THE SERVER
MY-SOCKET
> (defparameter my-stream (socket-accept my-socket)) ;ON THE SERVER
> (defparameter my-stream (socket-connect 4321 "127.0.0.1")) ;ON THE CLIENT
MY-STREAM
> (print "Yo Server!" my-stream) ;ON THE CLIENT
"Yo Server!"
> (read my-stream) ;ON THE SERVER
"Yo Server!"
> (close my-stream)
T
chap 13
写了一个简单的web server
包括解析url,解析参数,解析http header,根据请求参数进行应答等等
chap 14
介绍函数式编程
chap 15
又是一个游戏
chap 16 宏
;This function is finally safe to use其中,gensym是为了避免调用的时候也有变量名为g,导致冲突出错
(defmacro split (val yes no)
(let1 g (gensym)
`(let1 ,g ,val
(if ,g
(let ((head (car ,g))
(tail (cdr ,g)))
,yes)
,no))))
chap 17
DSL介绍,举了svg的例子,大概就是新建一些函数or宏,生成特定格式的文件or转换为特定格式的内容
比较灵活,容易自行扩展,但只适用于特定领域
chap 18
延迟求值
chap 19
游戏例子,没看
chap 20
还是游戏例子,没看
没有评论:
发表评论