- 列表(Lists)
- 序列(Sequences)
- 断言:
every
,some
…… - 函数合集
- length(sequence)
- elt(sequence, index)
- count(foo, sequence)
- subseq(sequence start, [end])
- sort, stable-sort(sequence, test [, key function])
- find, position(foo, sequence)
- find-if, find-if-not, position-if, position-if-not(test sequence)
- search(sequence-a, sequence-b)
- substitude, nsubstitute[if, if-not]
- sort, stable-sort, merge
- replace(sequence-a, sequence-b)
- remove, delete(foo sequence)
- mapping(
map
mapcar
remove-if[-not]
……) - reduce(function, sequence)
- 使用变量创建列表
- 断言:
- 集合(Set)
- 数组和向量(Array、vectors)
- 哈希表(Hash Table)
- Alist
- Plist
- 结构体(Structres)
- 树
列表(Lists)
构建列表
最基本的构建列表的元素是 cons
,其中 cons
结构中有两个元素,第一个叫做 car
,第二个叫做 cdr
(cons 1 2)
;; => (1 . 2) ;; representation with a point, a dotted pair.
(car (cons 1 2))
;; => 1
(cdr (cons 1 2))
;; => 2
cons 的构造可以这样理解
[o|o]--- 2
|
1
当 cons
的第二个元素(cdr
)指向的是另外一个 cons
,同时这个 cons 的第二个元素是 nil
的话,这样就构成了一个列表。
(cons 1 (cons 2 nil))
;; => (1 2)
(car (cons 1 (cons 2 nil)))
;; => 1
(cdr (cons 1 (cons 2 nil)))
;; => (2)
这个列表的结构如下:
[o|o]---[o|/]
| |
1 2
有注意到列表的输出中间没有一个点吗?Lisp 的解释器能够区别这个规则。
当然,也可以通过 list
直接构建一个列表
(list 1 2)
;; => (1 2)
或者是通过单引号
'(1 2)
;; => (1 2)
这个是 (quote (1 2))
的简写
循环列表
cons 结构中的 car 和 cdr 可以指向其他的结构,包括其 cons 本身或是同一个列表中的其他元素。因此,就可以定义像循环列表这样的指向自身的结构。
在构建循环列表之前,需要将 print-circle 这个变量的值设为 T
,这样解释器就能够识别循环列表了。
(setf *print-circle* t)
这样,就可以定义一个构建循环列表的函数了
(defun circular! (items)
"Modifies the last cdr of list ITEMS, returning a circular list"
(setf (cdr (last items)) items))
(circular! (list 1 2 3))
;; => #1=(1 2 3 . #1#)
(fifth (circular! (list 1 2 3)))
;; => 2
list-length这个函数能够识别出循环列表,然后返回 nil
。
在解释器中可以使用 [#n=](http://www.lispworks.com/documentation/HyperSpec/Body/02_dho.htm)
直接构建循环列表。其中 n
是一个未被使用过的十进制整数,#n#
可以指向本身
'#42=(1 2 3 . #42#)
;; => #1=(1 2 3 . #1#)
注意,解释器中输入的 n=42
会被销毁掉,解释器会重新定义一个新的标签(n=1
)。
深入理解循环表达式:Let over Lambda
car/cdr
或者 first/rest
以及 second
到 tenth
(car (cons 1 2)) ;; => 1
(cdr (cons 1 2)) ;; => 2
(first (cons 1 2)) ;; => 1
(first '(1 2 3)) ;; => 1
(rest '(1 2 3)) ;; => (2 3)
可以通过 setf
来进行赋值。
last
、butlast
、nbutlast
(n为可选参数)
返回列表中的最后一个(或是倒数第 n 个) cons
结构。
(last '(1 2 3))
;; => (3)
(car (last '(1 2 3)) ) ;; or (first (last …))
;; => 3
(butlast '(1 2 3))
;; => (1 2)
在 Alexandria 包中, lastcar
等价于 (first (last ...))
。
(alexandria:lastcar '(1 2 3))
;; => 3
倒序(reverse
、nreverse
)
reverse
和 nreverse
会返回一个序列的倒序。其中 nreverse
会破坏原有序列的结构,N 是 non-consing ,表示该函数不会去申请新的 cons 结构,通常是直接在原有的序列上做改动。
(defparameter mylist '(1 2 3))
;; => (1 2 3)
(reverse mylist)
;; => (3 2 1)
mylist
;; => (1 2 3)
(nreverse mylist)
;; => (3 2 1)
mylist
;; => (1)
追加(append
)
append
可以接受多个列表参数,然后返回一个包含所有参数的列表。
(append (list 1 2) (list 3 4))
;; => (1 2 3 4)
栈操作(push
pop
)
push
将值放在列表的最前面,然后把得到的新列表存在一个位置并打印输出
defparameter mylist '(1 2 3))
(push 0 mylist)
;; => (0 1 2 3)
(defparameter x '(a (b c) d))
;; => (A (B C) D)
(push 5 (cadr x))
;; => (5 B C)
x
;; => (A (5 B C) D)
push
等价于 (setf place (cons item place))
,其中子结构 (cons item place)
中 place
只会求值一次,同时 item
的求值要在 palce
之前。
pop
与 push
相反,会将列表中的第一个元素取出,同时也会破坏列表的结构。
索引(nthcdr
)
当 first
、second
… tenth
不够用时,可以使用 nthcdr
(defparameter mylist '(1 2 3 4 5 6 7 8 9 10 11))
(nthcdr 10 mylist)
car/cdr 嵌套(cadr
,caadr
……)
(caar (list 1 2 3)) ==> error
(caar (list (list 1 2) 3)) ==> 1
(cadr (list (list 1 2) (list 3 4))) ==> (3 4)
(caadr (list (list 1 2) (list 3 4))) ==> 3
destructuring-bind
destructuring-bind
可以将参数绑定到列表中
(destructuring-bind (x y z) (list 1 2 3)
(list :x x :y y :z z))
;; => (:X 1 :Y 2 :Z 3)
同时也可以匹配到子列表
(destructuring-bind (x (y1 y2) z) (list 1 (list 2 20) 3)
(list :x x :y1 y1 :y2 y2 :z z))
;; => (:X 1 :Y1 2 :Y2 20 :Z 3)
参数列表中也可以使用 &optional
、&rest
、key
和&whole
(destructuring-bind (x (y1 &optional y2) z) (list 1 (list 2) 3)
(list :x x :y1 y1 :y2 y2 :z z))
;; => (:X 1 :Y1 2 Y2: NIL :Z 3)
(destructuring-bind (&key x y z) (list :z 1 :y 2 :x 3)
(list :x x :y y :z z))
;; => (:X 3 :Y 2 :Z 1)
(destructuring-bind (&whole whole-list &key x y z) (list :z 1 :y 2 :x 3)
(list :x x :y y :z z :whole whole-list))
;; => (:X 3 :Y 2 :Z 1 :WHOLE-LIST (:Z 1 :Y 2 :X 3))
(destructuring-bind (&key a (b :not-found) c
&allow-other-keys)
'(:c 23 :d "D" :a #\A :foo :whatever)
(list a b c))
;; => (#\A :NOT-FOUND 23)
如果需要进行模式匹配的话,见 模式匹配 章节
断言(null
,listp
)
null
等价于 not
,但是 null
的格式更好listp
判断对象是 cons 还是 nil
还有一些其他的序列的断言: ldiff
tailp
list*
make-list
fill
revappend
nreconc
consp
atom
(make-list 3 :initial-element "ta")
;; => ("ta" "ta" "ta")
(make-list 3)
;; => (NIL NIL NIL)
(fill * "hello")
;; => ("hello" "hello" "hello")
member (elt, list)
返回匹配到列表元素及其后的元素,可以使用 :test
:test-not
:key
这样的参数
(member 2 '(1 2 3))
;; (2 3)
替换
subst
和 subst-if
会搜索匹配列表中的元素,并将匹配到的元素进行替换
(subst 'one 1 '(1 2 3))
;; => (ONE 2 3)
(subst 'one 1 '(1 2 1))
;; => (ONE 2 ONE)
(subst '(1 . one) '(1 . 1) '((1 . 1) (2 . 2) (3 . 3)) :test #'equal)
;; ((1 . ONE) (2 . 2) (3. 3))
sublis
是同时替换多个对象,可以使用 :test
和 :key
(sublis '((x . 10) (y . 20))
'(* x (+ x y) (* y y)))
;; (* 10 (+ 10 20) (* 20 20))
(sublis '((t . "foo"))
'("one" 2 ("three" (4 5)))
:key #'stringp)
;; ("foo" 2 ("foo" (4 5)))
序列(Sequences)
列表(list)和向量(vectors)(因此字符窜(string))都是序列。
大部分序列的函数都可以使用关键词参数,且都是可选的,放置顺序也比较随意。
特别注意 :test
参数,该参数默认的函数是 eql
(字符串的话是 :equal
)
:key
参数需要传递 nil
或是函数。
(find x y :key 'car)
和 (assoc* x y)
相似,在列表中搜索元素,检查列表中元素的 car
是否于 x
相等, 而不是列表中的元素等于 x
。如果 :key
没有写,或者为 nil
时,过滤函数就是 nil
。
(defparameter my-alist (list (cons 'foo "foo")
(cons 'bar "bar")))
;; => ((FOO . "foo") (BAR . "bar"))
(find 'bar my-alist)
;; => nil
(find 'bar my-alist :key 'car)
;; => (BAR . "bar")
(find (car my-alist) my-alist :key 'nil)
;; => (FOO . "foo")
(find (car my-alist) my-alist)
;; => (FOO . "foo")
使用 lambda
函数
(find 'bar my-alist :key (lambda (it) (car it)))
注:cl21标准中,可将 lambda
简写:
(find 'bar my-alist :key ^(car %))
(find 'bar my-alist :key (lm (it) (cat it)))
断言:every
,some
……
every, notevery (test, sequence)
:返回 nil 或 t。相对应的,对序列的任何一组对应元素进行一次测试,返回 nil。
(defparameter foo '(1 2 3))
(every #'evenp foo)
;; => NIL
(some #'evenp foo)
;; => T
(defparameter str '("foo" "bar" "team"))
(every #'stringp str)
;; => T
(some #'(lambda (it) (= 3 (length it))) str)
;; => T
(some ^(= 3 (length %)) str) ;; in CL21
;; => T
some
、notany
(test, sequence):如果测试结果中又一个为 t,返回 t,否则为 nil。
mismatch
(sequence-a, sequence-b):返回 sequence-a 在 sequence-b 中第一次匹配后的位置。如果sequence-a 和 sequence-b 完全匹配的话,返回 NIL。可以使用参数::from-end bool
、start1
、start2
以及 :end[1, 2]
函数合集
在 Alexandria包中定义了很多序列相关的函数:starts-with
,ends-with
,ends-with-subseq
,length=
,emptyp
……
length(sequence)
elt(sequence, index)
注意,这里第一个参数是序列
count(foo, sequence)
返回 sequences 中匹配了 foo 的个数。额外参数::from-end
,:start
,:end
subseq(sequence start, [end])
在产生的结果和 sequence 的长度相同时,可以使用 setf。
sort, stable-sort(sequence, test [, key function])
该函数会破坏参数的结构,所以最好对拷贝的序列做操作
(sort (copy-seq seq) :test #'string<)
find, position(foo, sequence)
(find 20 '(10 20 30))
;; 20
(position 20 '(10 20 30))
find-if, find-if-not, position-if, position-if-not(test sequence)
search(sequence-a, sequence-b)
返回 sequence-a 在 sequence-b 中的位置,若不匹配,则为 NIL。支持 from-end
,end1/2
等参数。
substitude, nsubstitute[if, if-not]
(substitute #\o #\x "hellx") ;; => "hello"
(substitute :a :x '(:a :x :x)) ;; => (:A :A :A)
(substitude "a" "x" '("a" "x" "x") :test #'string=) ;; => ("a" "a" "a")
sort, stable-sort, merge
replace(sequence-a, sequence-b)
remove, delete(foo sequence)
delete
是 remove
的回收版。支持 :start/end
,:key
,:count
。
(remove "foo" '("foo" "bar" "foo") :test 'equal)
;; => ("bar")
mapping(map
mapcar
remove-if[-not]
……)
(defparameter foo '(1 2 3))
(map 'list (lambda (it) (* 10 it)) foo)
reduce(function, sequence)
特殊参数 :initial-value
(reduce '- '(1 2 3 4))
;; => -8
(reduce '- '(1 2 3 4) :initial-value 100)
;; => 90
使用变量创建列表
这是 反引用(backquote)
的用法之一
(defparameter *var* "bar")
;; First try:
'("foo" *var* "baz") ;; no backquote
;; => ("foo" *VAR* "baz") ;; nope
`("foo" ,*var* "baz") ;; backquote, comma
;; => ("foo" "bar" "baz") ;; good
反引号标志着只是插入的,逗号表示使用变量的值。
如果变量是一个列表的话:
(defparameter *var* '("bar" "baz"))
;; First try:
`("foo" ,*var*)
;; => ("foo" ("bar" "baz")) ;; nested list
`("foo" ,@*var*) ;; backquote, comma-@ to
;; => ("foo" "bar" "baz")
集合(Set)
交集:intersection
(defparameter list-a '(0 1 2 3))
(defparameter list-b '(0 2 4))
(intersection list-a list-b)
;; => (2 0)
差集:set-difference
(set-difference list-a list-b)
;; => (3 1)
(set-difference list-b list-a)
;; => (4)
并集:union
(union list-a list-b)
;; => (3 1 0 2 4) ;; order can be different in your lisp
补集:set-exclusive-or
(set-exclusive-or list-a list-b)
;; => (4 3 1)
数组和向量(Array、vectors)
数组
数组的特性是:访问时间为常数(θ(1))
简单的数组既不可替换(:displaced-to
,指向以另一个数组),也不可变(:adjust-array
),同时也没有填充指针(fill-pointer
, 随着元素的增减而进行相应的改变)。
向量 是一维的数组。
make-array
(sizes-list :adjustable bool)adjust-array
(array, sizes-list, :element-type, :initial-element)aref
(array i [j …])aref
(array i j k …) 或row-major-aref
(array i) 等价于(aref i i i ...)
(defparameter myarray (make-array '(2 2 2) :initial-element 1))
myarray
;; => #3A(((1 1) (1 1)) ((1 1) (1 1)))
(aref myarray 0 0 0 )
;; => 1
(setf (aref myarray 0 0 0) 9)
;; => 9
(row-major-aref myarray 0)
;; => 9
array-demensions
(array): 数组的维度列表,即数组中元素的总个数array-demension
(array):每维数组的大小(defparameter myarray (make array '(2 2 2)))
;; => MYARRAY
myarray
;; => #3A(((0 0) (0 0)) ((0 0) (0 0)))
(array-rank myarray)
;; => 3
(array-dimensions myarray)
;; => (2 2 2)
(array-dimension myarray 0)
;; => 2
(array-total-size myarray)
;; => 8
向量
(vector 1 2 3)
;; => #(1 2 3)
#(1 2 3)
;; => #(1 2 3)
vector-push
(foo vector)vector-push-extend
(foo vector [extension-num])vector-pop
(vector)fill-pointer
(vector)coerce
(vector ‘list):将向量转换成列表
哈希表(Hash Table)
创建哈希表 make-hash-table
(defvar *my-hash* (make-hash-table))
(setf (gethash :name *my-hash*) "Eitaro Fukamachi")
获取哈希值
(gethash :name *my-hash*)
当键值对不存在时
(gethash 'bar *my-hash* "default-bar")
;; => "default-bar"
;; NIL
获取哈希表中的键值对
(ql:quickload :alexandria)
;; [ ... ]
(alexandria:hash-table-keys *my-hash*)
;; => (:NAME)
添加元素
CL-USER> (defparameter *my-hash* (make-hash-table))
*MY-HASH*
CL-USER> (setf (gethash 'one-entry *my-hash*) "one")
"one"
CL-USER> (setf (gethash 'another-entry *my-hash*) 2/4)
1/2
CL-USER> (gethash 'one-entry *my-hash*)
"one"
T
CL-USER> (gethash 'another-entry *my-hash*)
1/2
T
CL-USER> (defparameter *my-hash* (make-hash-table))
*MY-HASH*
CL-USER> (setf (gethash 'one-entry *my-hash*) "one")
"one"
CL-USER> (if (gethash 'one-entry *my-hash*)
"Key exists"
"Key does not exist")
"Key exists"
CL-USER> (if (gethash 'another-entry *my-hash*)
"Key exists"
"Key does not exist")
"Key does not exist"
CL-USER> (setf (gethash 'another-entry *my-hash*) nil)
NIL
CL-USER> (if (gethash 'another-entry *my-hash*)
"Key exists"
"Key does not exist")
"Key does not exist"
CL-USER> (if (nth-value 1 (gethash 'another-entry *my-hash*))
"Key exists"
"Key does not exist")
"Key exists"
CL-USER> (if (nth-value 1 (gethash 'no-entry *my-hash*))
"Key exists"
"Key does not exist")
"Key does not exist"
删除元素
CL-USER> (defparameter *my-hash* (make-hash-table))
*MY-HASH*
CL-USER> (setf (gethash 'first-key *my-hash*) 'one)
ONE
CL-USER> (gethash 'first-key *my-hash*)
ONE
T
CL-USER> (remhash 'first-key *my-hash*)
T
CL-USER> (gethash 'first-key *my-hash*)
NIL
NIL
CL-USER> (gethash 'no-entry *my-hash*)
NIL
NIL
CL-USER> (remhash 'no-entry *my-hash*)
NIL
CL-USER> (gethash 'no-entry *my-hash*)
NIL
NIL
遍历哈希表
CL-USER> (defparameter *my-hash* (make-hash-table))
*MY-HASH*
CL-USER> (setf (gethash 'first-key *my-hash*) 'one)
ONE
CL-USER> (setf (gethash 'second-key *my-hash*) 'two)
TWO
CL-USER> (setf (gethash 'third-key *my-hash*) nil)
NIL
CL-USER> (setf (gethash nil *my-hash*) 'nil-value)
NIL-VALUE
CL-USER> (defun print-hash-entry (key value)
(format t "The value associated with the key ~S is ~S~%" key value))
PRINT-HASH-ENTRY
CL-USER> (maphash #'print-hash-entry *my-hash*)
The value associated with the key FIRST-KEY is ONE
The value associated with the key SECOND-KEY is TWO
The value associated with the key THIRD-KEY is NIL
The value associated with the key NIL is NIL-VALUE
;;; same hash-table as above
CL-USER> (with-hash-table-iterator (my-iterator *my-hash*)
(loop
(multiple-value-bind (entry-p key value)
(my-iterator)
(if entry-p
(print-hash-entry key value)
(return)))))
The value associated with the key FIRST-KEY is ONE
The value associated with the key SECOND-KEY is TWO
The value associated with the key THIRD-KEY is NIL
The value associated with the key NIL is NIL-VALUE
NIL
;;; same hash-table as above
CL-USER> (loop for key being the hash-keys of *my-hash*
do (print key))
FIRST-KEY
SECOND-KEY
THIRD-KEY
NIL
NIL
CL-USER> (loop for key being the hash-keys of *my-hash*
using (hash-value value)
do (format t "The value associated with the key ~S is ~S~%" key value) )
The value associated with the key FIRST-KEY is ONE
The value associated with the key SECOND-KEY is TWO
The value associated with the key THIRD-KEY is NIL
The value associated with the key NIL is NIL-VALUE
NIL
CL-USER> (loop for value being the hash-values of *my-hash*
do (print value))
ONE
TWO
NIL
NIL-VALUE
NIL
CL-USER> (loop for value being the hash-values of *my-hash*
using (hash-key key)
do (format t "~&~A -> ~A" key value))
FIRST-KEY -> ONE
SECOND-KEY -> TWO
THIRD-KEY -> NIL
NIL -> NIL-VALUE
NIL
获取哈希表键值对的数量:hash-table-count
CL-USER> (defparameter *my-hash* (make-hash-table))
*MY-HASH*
CL-USER> (hash-table-count *my-hash*)
0
CL-USER> (setf (gethash 'first *my-hash*) 1)
1
CL-USER> (setf (gethash 'second *my-hash*) 2)
2
CL-USER> (setf (gethash 'third *my-hash*) 3)
3
CL-USER> (hash-table-count *my-hash*)
3
CL-USER> (setf (gethash 'second *my-hash*) 'two)
TWO
CL-USER> (hash-table-count *my-hash*)
3
CL-USER> (clrhash *my-hash*)
#<EQL hash table, 0 entries {48205F35}>
CL-USER> (hash-table-count *my-hash*)
0
性能
CL-USER> (defparameter *my-hash* (make-hash-table))
*MY-HASH*
CL-USER> (hash-table-size *my-hash*)
65
CL-USER> (hash-table-rehash-size *my-hash*)
1.5
CL-USER> (time (dotimes (n 100000) (setf (gethash n *my-hash*) n)))
Compiling LAMBDA NIL:
Compiling Top-Level Form:
Evaluation took:
0.27 seconds of real time
0.25 seconds of user run time
0.02 seconds of system run time
0 page faults and
8754768 bytes consed.
NIL
CL-USER> (time (dotimes (n 100000) (setf (gethash n *my-hash*) n)))
Compiling LAMBDA NIL:
Compiling Top-Level Form:
Evaluation took:
0.05 seconds of real time
0.05 seconds of user run time
0.0 seconds of system run time
0 page faults and
0 bytes consed.
NIL
CL-USER> (log (/ 100000 65) 1.5)
18.099062
CL-USER> (let ((size 65)) (dotimes (n 20) (print (list n size)) (setq size (* 1.5 size))))
(0 65)
(1 97.5)
(2 146.25)
(3 219.375)
(4 329.0625)
(5 493.59375)
(6 740.3906)
(7 1110.5859)
(8 1665.8789)
(9 2498.8184)
(10 3748.2275)
(11 5622.3413)
(12 8433.512)
(13 12650.268)
(14 18975.402)
(15 28463.104)
(16 42694.656)
(17 64041.984)
(18 96062.98)
(19 144094.47)
NIL
CL-USER> (defparameter *my-hash* (make-hash-table :size 100000))
*MY-HASH*
CL-USER> (hash-table-size *my-hash*)
100000
CL-USER> (time (dotimes (n 100000) (setf (gethash n *my-hash*) n)))
Compiling LAMBDA NIL:
Compiling Top-Level Form:
Evaluation took:
0.04 seconds of real time
0.04 seconds of user run time
0.0 seconds of system run time
0 page faults and
0 bytes consed.
NIL
CL-USER> (defparameter *my-hash* (make-hash-table :rehash-size 100000))
*MY-HASH*
CL-USER> (hash-table-size *my-hash*)
65
CL-USER> (hash-table-rehash-size *my-hash*)
100000
CL-USER> (time (dotimes (n 100000) (setf (gethash n *my-hash*) n)))
Compiling LAMBDA NIL:
Compiling Top-Level Form:
Evaluation took:
0.07 seconds of real time
0.05 seconds of user run time
0.01 seconds of system run time
0 page faults and
2001360 bytes consed.
NIL
Alist
Alist: An association list is a list of cons cells.
(defparameter *my-alist* (list (cons 'foo "foo")
(cons 'bar "bar")))
;; => ((FOO . "foo") (BAR . "bar"))
其结构如下:
[o|o]---[o|/]
| |
| [o|o]---"bar"
| |
| BAR
|
[o|o]---"foo"
|
FOO
所以,通过其结构来对其进行定义:
(setf *my-alist* '((:foo . "foo")
(:bar . "bar")))
Plist
Plist: A property list is simply a list that alternates a key, a value, and so on, where its keys are symbols (we can not set its :test). More precisely, it first has a cons cell whose car
is the key, whose cdr
points to the following cons cell whose car
is the value.
(defparameter my-plist (list 'foo "foo" 'bar "bar"))
[o|o]---[o|o]---[o|o]---[o|/]
| | | |
FOO "foo" BAR "bar"
(defparameter my-plist (list 'foo "foo" 'bar "bar"))
;; => (FOO "foo" BAR "bar")
(setf (getf my-plist 'foo) "foo!!!")
;; => "foo!!!"
结构体(Structres)
创建:defstruct
(defstruct person
id name age)
设置默认值
(defstruct person
id
(name "john doe")
age)
设置默认类型
(defstruct person
id
(name "john doe" :type string)
age)
(defparameter *me* (make-person))
*me*
#S(PERSON :ID NIL :NAME "john doe" :AGE NIL)
当类型设置错误时:
(defparameter *bad-name* (make-person :name 123))
Invalid initialization argument:
:NAME
in call for class #<STRUCTURE-CLASS PERSON>.
[Condition of type SB-PCL::INITARG-ERROR]
不使用关键次参数来创建结构体
(defstruct (person (:constructor create-person (id name age)))
id
name
age)
(create-person 1 "me" 7)
#S(PERSON :ID 1 :NAME "me" :AGE 7)
然而,默认的 make-person
不可以使用
(make-person :name "me")
;; debugger:
obsolete structure error for a structure of type PERSON
[Condition of type SB-PCL::OBSOLETE-STRUCTURE]
访问 槽(slot)
(person-name *me*)
;; "john doe"
(setf (person-name *me*) "Cookbook author")
(person-name *me*)
;; "Cookbook author"
断言
在创建结构时,一个断言函数会自动创建
(person-p *me*)
T
单继承
(defstruct (female (:include person))
(gender "female" :type string))
(make-female :name "Lilie")
;; #S(FEMALE :ID NIL :NAME "Lilie" :AGE NIL :GENDER "female")
局限
修改后,实例不会被更新
(defstruct person
id
(name "john doe" :type string)
age
email)
attempt to redefine the STRUCTURE-OBJECT class PERSON
incompatibly with the current definition
[Condition of type SIMPLE-ERROR]
Restarts:
0: [CONTINUE] Use the new definition of PERSON, invalidating already-loaded code and instances.
1: [RECKLESSLY-CONTINUE] Use the new definition of PERSON as if it were compatible, allowing old accessors to use new instances and allowing new accessors to use old instances.
2: [CLOBBER-IT] (deprecated synonym for RECKLESSLY-CONTINUE)
3: [RETRY] Retry SLIME REPL evaluation request.
4: [*ABORT] Return to SLIME's top level.
5: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING {1002A0FFA3}>)
如果重新开始的话,使用新的定义,将无法访问 *me*
*me*
obsolete structure error for a structure of type PERSON
[Condition of type SB-PCL::OBSOLETE-STRUCTURE]
树
tree-equal
copy-tree