列表(Lists)

构建列表

最基本的构建列表的元素是 cons,其中 cons 结构中有两个元素,第一个叫做 car,第二个叫做 cdr

  1. (cons 1 2)
  2. ;; => (1 . 2) ;; representation with a point, a dotted pair.
  3. (car (cons 1 2))
  4. ;; => 1
  5. (cdr (cons 1 2))
  6. ;; => 2

cons 的构造可以这样理解

  1. [o|o]--- 2
  2. |
  3. 1

cons 的第二个元素(cdr)指向的是另外一个 cons,同时这个 cons 的第二个元素是 nil 的话,这样就构成了一个列表。

  1. (cons 1 (cons 2 nil))
  2. ;; => (1 2)
  3. (car (cons 1 (cons 2 nil)))
  4. ;; => 1
  5. (cdr (cons 1 (cons 2 nil)))
  6. ;; => (2)

这个列表的结构如下:

  1. [o|o]---[o|/]
  2. | |
  3. 1 2

有注意到列表的输出中间没有一个点吗?Lisp 的解释器能够区别这个规则。

当然,也可以通过 list 直接构建一个列表

  1. (list 1 2)
  2. ;; => (1 2)

或者是通过单引号

  1. '(1 2)
  2. ;; => (1 2)

这个是 (quote (1 2)) 的简写

循环列表

cons 结构中的 car 和 cdr 可以指向其他的结构,包括其 cons 本身或是同一个列表中的其他元素。因此,就可以定义像循环列表这样的指向自身的结构。

在构建循环列表之前,需要将 print-circle 这个变量的值设为 T,这样解释器就能够识别循环列表了。

  1. (setf *print-circle* t)

这样,就可以定义一个构建循环列表的函数了

  1. (defun circular! (items)
  2. "Modifies the last cdr of list ITEMS, returning a circular list"
  3. (setf (cdr (last items)) items))
  4. (circular! (list 1 2 3))
  5. ;; => #1=(1 2 3 . #1#)
  6. (fifth (circular! (list 1 2 3)))
  7. ;; => 2

list-length这个函数能够识别出循环列表,然后返回 nil

在解释器中可以使用 [#n=](http://www.lispworks.com/documentation/HyperSpec/Body/02_dho.htm) 直接构建循环列表。其中 n 是一个未被使用过的十进制整数,#n# 可以指向本身

  1. '#42=(1 2 3 . #42#)
  2. ;; => #1=(1 2 3 . #1#)

注意,解释器中输入的 n=42 会被销毁掉,解释器会重新定义一个新的标签(n=1)。

深入理解循环表达式:Let over Lambda

car/cdr 或者 first/rest 以及 secondtenth

  1. (car (cons 1 2)) ;; => 1
  2. (cdr (cons 1 2)) ;; => 2
  3. (first (cons 1 2)) ;; => 1
  4. (first '(1 2 3)) ;; => 1
  5. (rest '(1 2 3)) ;; => (2 3)

可以通过 setf 来进行赋值。

lastbutlastnbutlast(n为可选参数)

返回列表中的最后一个(或是倒数第 n 个) cons 结构。

  1. (last '(1 2 3))
  2. ;; => (3)
  3. (car (last '(1 2 3)) ) ;; or (first (last …))
  4. ;; => 3
  5. (butlast '(1 2 3))
  6. ;; => (1 2)

Alexandria 包中, lastcar 等价于 (first (last ...))

  1. (alexandria:lastcar '(1 2 3))
  2. ;; => 3

倒序(reversenreverse

reversenreverse 会返回一个序列的倒序。其中 nreverse 会破坏原有序列的结构,N 是 non-consing ,表示该函数不会去申请新的 cons 结构,通常是直接在原有的序列上做改动。

  1. (defparameter mylist '(1 2 3))
  2. ;; => (1 2 3)
  3. (reverse mylist)
  4. ;; => (3 2 1)
  5. mylist
  6. ;; => (1 2 3)
  7. (nreverse mylist)
  8. ;; => (3 2 1)
  9. mylist
  10. ;; => (1)

追加(append

append 可以接受多个列表参数,然后返回一个包含所有参数的列表。

  1. (append (list 1 2) (list 3 4))
  2. ;; => (1 2 3 4)

栈操作(push pop

push 将值放在列表的最前面,然后把得到的新列表存在一个位置并打印输出

  1. defparameter mylist '(1 2 3))
  2. (push 0 mylist)
  3. ;; => (0 1 2 3)
  1. (defparameter x '(a (b c) d))
  2. ;; => (A (B C) D)
  3. (push 5 (cadr x))
  4. ;; => (5 B C)
  5. x
  6. ;; => (A (5 B C) D)

push 等价于 (setf place (cons item place)) ,其中子结构 (cons item place)place 只会求值一次,同时 item 的求值要在 palce 之前。

poppush 相反,会将列表中的第一个元素取出,同时也会破坏列表的结构。

索引(nthcdr

firstsecondtenth 不够用时,可以使用 nthcdr

  1. (defparameter mylist '(1 2 3 4 5 6 7 8 9 10 11))
  2. (nthcdr 10 mylist)

car/cdr 嵌套(cadrcaadr……)

  1. (caar (list 1 2 3)) ==> error
  2. (caar (list (list 1 2) 3)) ==> 1
  3. (cadr (list (list 1 2) (list 3 4))) ==> (3 4)
  4. (caadr (list (list 1 2) (list 3 4))) ==> 3

destructuring-bind

destructuring-bind 可以将参数绑定到列表中

  1. (destructuring-bind (x y z) (list 1 2 3)
  2. (list :x x :y y :z z))
  3. ;; => (:X 1 :Y 2 :Z 3)

同时也可以匹配到子列表

  1. (destructuring-bind (x (y1 y2) z) (list 1 (list 2 20) 3)
  2. (list :x x :y1 y1 :y2 y2 :z z))
  3. ;; => (:X 1 :Y1 2 :Y2 20 :Z 3)

参数列表中也可以使用 &optional&restkey&whole

  1. (destructuring-bind (x (y1 &optional y2) z) (list 1 (list 2) 3)
  2. (list :x x :y1 y1 :y2 y2 :z z))
  3. ;; => (:X 1 :Y1 2 Y2: NIL :Z 3)
  1. (destructuring-bind (&key x y z) (list :z 1 :y 2 :x 3)
  2. (list :x x :y y :z z))
  3. ;; => (:X 3 :Y 2 :Z 1)
  1. (destructuring-bind (&whole whole-list &key x y z) (list :z 1 :y 2 :x 3)
  2. (list :x x :y y :z z :whole whole-list))
  3. ;; => (:X 3 :Y 2 :Z 1 :WHOLE-LIST (:Z 1 :Y 2 :X 3))
  1. (destructuring-bind (&key a (b :not-found) c
  2. &allow-other-keys)
  3. '(:c 23 :d "D" :a #\A :foo :whatever)
  4. (list a b c))
  5. ;; => (#\A :NOT-FOUND 23)

如果需要进行模式匹配的话,见 模式匹配 章节

断言(nulllistp

null 等价于 not,但是 null 的格式更好
listp 判断对象是 cons 还是 nil

还有一些其他的序列的断言: ldiff tailp list* make-list fill revappend nreconc consp atom

  1. (make-list 3 :initial-element "ta")
  2. ;; => ("ta" "ta" "ta")
  3. (make-list 3)
  4. ;; => (NIL NIL NIL)
  5. (fill * "hello")
  6. ;; => ("hello" "hello" "hello")

member (elt, list)

返回匹配到列表元素及其后的元素,可以使用 :test :test-not :key 这样的参数

  1. (member 2 '(1 2 3))
  2. ;; (2 3)

替换

substsubst-if 会搜索匹配列表中的元素,并将匹配到的元素进行替换

  1. (subst 'one 1 '(1 2 3))
  2. ;; => (ONE 2 3)
  3. (subst 'one 1 '(1 2 1))
  4. ;; => (ONE 2 ONE)
  5. (subst '(1 . one) '(1 . 1) '((1 . 1) (2 . 2) (3 . 3)) :test #'equal)
  6. ;; ((1 . ONE) (2 . 2) (3. 3))

sublis 是同时替换多个对象,可以使用 :test:key

  1. (sublis '((x . 10) (y . 20))
  2. '(* x (+ x y) (* y y)))
  3. ;; (* 10 (+ 10 20) (* 20 20))
  4. (sublis '((t . "foo"))
  5. '("one" 2 ("three" (4 5)))
  6. :key #'stringp)
  7. ;; ("foo" 2 ("foo" (4 5)))

序列(Sequences)

列表(list)向量(vectors)(因此字符窜(string))都是序列。

大部分序列的函数都可以使用关键词参数,且都是可选的,放置顺序也比较随意。

特别注意 :test 参数,该参数默认的函数是 eql(字符串的话是 :equal

:key 参数需要传递 nil 或是函数。

  1. (find x y :key 'car)

(assoc* x y) 相似,在列表中搜索元素,检查列表中元素的 car 是否于 x 相等, 而不是列表中的元素等于 x。如果 :key 没有写,或者为 nil 时,过滤函数就是 nil

  1. (defparameter my-alist (list (cons 'foo "foo")
  2. (cons 'bar "bar")))
  3. ;; => ((FOO . "foo") (BAR . "bar"))
  4. (find 'bar my-alist)
  5. ;; => nil
  6. (find 'bar my-alist :key 'car)
  7. ;; => (BAR . "bar")
  8. (find (car my-alist) my-alist :key 'nil)
  9. ;; => (FOO . "foo")
  10. (find (car my-alist) my-alist)
  11. ;; => (FOO . "foo")

使用 lambda 函数

  1. (find 'bar my-alist :key (lambda (it) (car it)))

注:cl21标准中,可将 lambda 简写:

  1. (find 'bar my-alist :key ^(car %))
  2. (find 'bar my-alist :key (lm (it) (cat it)))

断言:everysome……

every, notevery (test, sequence) :返回 nil 或 t。相对应的,对序列的任何一组对应元素进行一次测试,返回 nil。

  1. (defparameter foo '(1 2 3))
  2. (every #'evenp foo)
  3. ;; => NIL
  4. (some #'evenp foo)
  5. ;; => T
  6. (defparameter str '("foo" "bar" "team"))
  7. (every #'stringp str)
  8. ;; => T
  9. (some #'(lambda (it) (= 3 (length it))) str)
  10. ;; => T
  11. (some ^(= 3 (length %)) str) ;; in CL21
  12. ;; => T

somenotany(test, sequence):如果测试结果中又一个为 t,返回 t,否则为 nil。

mismatch(sequence-a, sequence-b):返回 sequence-a 在 sequence-b 中第一次匹配后的位置。如果sequence-a 和 sequence-b 完全匹配的话,返回 NIL。可以使用参数::from-end boolstart1start2 以及 :end[1, 2]

函数合集

Alexandria包中定义了很多序列相关的函数:starts-withends-withends-with-subseqlength=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])

该函数会破坏参数的结构,所以最好对拷贝的序列做操作

  1. (sort (copy-seq seq) :test #'string<)

find, position(foo, sequence)

  1. (find 20 '(10 20 30))
  2. ;; 20
  3. (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-endend1/2等参数。

substitude, nsubstitute[if, if-not]

  1. (substitute #\o #\x "hellx") ;; => "hello"
  2. (substitute :a :x '(:a :x :x)) ;; => (:A :A :A)
  3. (substitude "a" "x" '("a" "x" "x") :test #'string=) ;; => ("a" "a" "a")

sort, stable-sort, merge

replace(sequence-a, sequence-b)

remove, delete(foo sequence)

deleteremove 的回收版。支持 :start/end:key:count

  1. (remove "foo" '("foo" "bar" "foo") :test 'equal)
  2. ;; => ("bar")

mapping(map mapcar remove-if[-not]……)

  1. (defparameter foo '(1 2 3))
  2. (map 'list (lambda (it) (* 10 it)) foo)

reduce(function, sequence)

特殊参数 :initial-value

  1. (reduce '- '(1 2 3 4))
  2. ;; => -8
  3. (reduce '- '(1 2 3 4) :initial-value 100)
  4. ;; => 90

使用变量创建列表

这是 反引用(backquote) 的用法之一

  1. (defparameter *var* "bar")
  2. ;; First try:
  3. '("foo" *var* "baz") ;; no backquote
  4. ;; => ("foo" *VAR* "baz") ;; nope
  5. `("foo" ,*var* "baz") ;; backquote, comma
  6. ;; => ("foo" "bar" "baz") ;; good

反引号标志着只是插入的,逗号表示使用变量的值。
如果变量是一个列表的话:

  1. (defparameter *var* '("bar" "baz"))
  2. ;; First try:
  3. `("foo" ,*var*)
  4. ;; => ("foo" ("bar" "baz")) ;; nested list
  5. `("foo" ,@*var*) ;; backquote, comma-@ to
  6. ;; => ("foo" "bar" "baz")

集合(Set)

交集:intersection

  1. (defparameter list-a '(0 1 2 3))
  2. (defparameter list-b '(0 2 4))
  3. (intersection list-a list-b)
  4. ;; => (2 0)

差集:set-difference

  1. (set-difference list-a list-b)
  2. ;; => (3 1)
  3. (set-difference list-b list-a)
  4. ;; => (4)

并集:union

  1. (union list-a list-b)
  2. ;; => (3 1 0 2 4) ;; order can be different in your lisp

补集:set-exclusive-or

  1. (set-exclusive-or list-a list-b)
  2. ;; => (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 ...)

    1. (defparameter myarray (make-array '(2 2 2) :initial-element 1))
    2. myarray
    3. ;; => #3A(((1 1) (1 1)) ((1 1) (1 1)))
    4. (aref myarray 0 0 0 )
    5. ;; => 1
    6. (setf (aref myarray 0 0 0) 9)
    7. ;; => 9
    8. (row-major-aref myarray 0)
    9. ;; => 9
  • array-demensions(array): 数组的维度列表,即数组中元素的总个数

  • array-demension(array):每维数组的大小
    1. (defparameter myarray (make array '(2 2 2)))
    2. ;; => MYARRAY
    3. myarray
    4. ;; => #3A(((0 0) (0 0)) ((0 0) (0 0)))
    5. (array-rank myarray)
    6. ;; => 3
    7. (array-dimensions myarray)
    8. ;; => (2 2 2)
    9. (array-dimension myarray 0)
    10. ;; => 2
    11. (array-total-size myarray)
    12. ;; => 8

向量

  1. (vector 1 2 3)
  2. ;; => #(1 2 3)
  3. #(1 2 3)
  4. ;; => #(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

  1. (defvar *my-hash* (make-hash-table))
  2. (setf (gethash :name *my-hash*) "Eitaro Fukamachi")

获取哈希值

  1. (gethash :name *my-hash*)

当键值对不存在时

  1. (gethash 'bar *my-hash* "default-bar")
  2. ;; => "default-bar"
  3. ;; NIL

获取哈希表中的键值对

  1. (ql:quickload :alexandria)
  2. ;; [ ... ]
  3. (alexandria:hash-table-keys *my-hash*)
  4. ;; => (:NAME)

添加元素

  1. CL-USER> (defparameter *my-hash* (make-hash-table))
  2. *MY-HASH*
  3. CL-USER> (setf (gethash 'one-entry *my-hash*) "one")
  4. "one"
  5. CL-USER> (setf (gethash 'another-entry *my-hash*) 2/4)
  6. 1/2
  7. CL-USER> (gethash 'one-entry *my-hash*)
  8. "one"
  9. T
  10. CL-USER> (gethash 'another-entry *my-hash*)
  11. 1/2
  12. T
  1. CL-USER> (defparameter *my-hash* (make-hash-table))
  2. *MY-HASH*
  3. CL-USER> (setf (gethash 'one-entry *my-hash*) "one")
  4. "one"
  5. CL-USER> (if (gethash 'one-entry *my-hash*)
  6. "Key exists"
  7. "Key does not exist")
  8. "Key exists"
  9. CL-USER> (if (gethash 'another-entry *my-hash*)
  10. "Key exists"
  11. "Key does not exist")
  12. "Key does not exist"
  1. CL-USER> (setf (gethash 'another-entry *my-hash*) nil)
  2. NIL
  3. CL-USER> (if (gethash 'another-entry *my-hash*)
  4. "Key exists"
  5. "Key does not exist")
  6. "Key does not exist"
  1. CL-USER> (if (nth-value 1 (gethash 'another-entry *my-hash*))
  2. "Key exists"
  3. "Key does not exist")
  4. "Key exists"
  5. CL-USER> (if (nth-value 1 (gethash 'no-entry *my-hash*))
  6. "Key exists"
  7. "Key does not exist")
  8. "Key does not exist"

删除元素

  1. CL-USER> (defparameter *my-hash* (make-hash-table))
  2. *MY-HASH*
  3. CL-USER> (setf (gethash 'first-key *my-hash*) 'one)
  4. ONE
  5. CL-USER> (gethash 'first-key *my-hash*)
  6. ONE
  7. T
  8. CL-USER> (remhash 'first-key *my-hash*)
  9. T
  10. CL-USER> (gethash 'first-key *my-hash*)
  11. NIL
  12. NIL
  13. CL-USER> (gethash 'no-entry *my-hash*)
  14. NIL
  15. NIL
  16. CL-USER> (remhash 'no-entry *my-hash*)
  17. NIL
  18. CL-USER> (gethash 'no-entry *my-hash*)
  19. NIL
  20. NIL

遍历哈希表

  1. CL-USER> (defparameter *my-hash* (make-hash-table))
  2. *MY-HASH*
  3. CL-USER> (setf (gethash 'first-key *my-hash*) 'one)
  4. ONE
  5. CL-USER> (setf (gethash 'second-key *my-hash*) 'two)
  6. TWO
  7. CL-USER> (setf (gethash 'third-key *my-hash*) nil)
  8. NIL
  9. CL-USER> (setf (gethash nil *my-hash*) 'nil-value)
  10. NIL-VALUE
  11. CL-USER> (defun print-hash-entry (key value)
  12. (format t "The value associated with the key ~S is ~S~%" key value))
  13. PRINT-HASH-ENTRY
  14. CL-USER> (maphash #'print-hash-entry *my-hash*)
  15. The value associated with the key FIRST-KEY is ONE
  16. The value associated with the key SECOND-KEY is TWO
  17. The value associated with the key THIRD-KEY is NIL
  18. The value associated with the key NIL is NIL-VALUE
  1. ;;; same hash-table as above
  2. CL-USER> (with-hash-table-iterator (my-iterator *my-hash*)
  3. (loop
  4. (multiple-value-bind (entry-p key value)
  5. (my-iterator)
  6. (if entry-p
  7. (print-hash-entry key value)
  8. (return)))))
  9. The value associated with the key FIRST-KEY is ONE
  10. The value associated with the key SECOND-KEY is TWO
  11. The value associated with the key THIRD-KEY is NIL
  12. The value associated with the key NIL is NIL-VALUE
  13. NIL
  1. ;;; same hash-table as above
  2. CL-USER> (loop for key being the hash-keys of *my-hash*
  3. do (print key))
  4. FIRST-KEY
  5. SECOND-KEY
  6. THIRD-KEY
  7. NIL
  8. NIL
  9. CL-USER> (loop for key being the hash-keys of *my-hash*
  10. using (hash-value value)
  11. do (format t "The value associated with the key ~S is ~S~%" key value) )
  12. The value associated with the key FIRST-KEY is ONE
  13. The value associated with the key SECOND-KEY is TWO
  14. The value associated with the key THIRD-KEY is NIL
  15. The value associated with the key NIL is NIL-VALUE
  16. NIL
  17. CL-USER> (loop for value being the hash-values of *my-hash*
  18. do (print value))
  19. ONE
  20. TWO
  21. NIL
  22. NIL-VALUE
  23. NIL
  24. CL-USER> (loop for value being the hash-values of *my-hash*
  25. using (hash-key key)
  26. do (format t "~&~A -> ~A" key value))
  27. FIRST-KEY -> ONE
  28. SECOND-KEY -> TWO
  29. THIRD-KEY -> NIL
  30. NIL -> NIL-VALUE
  31. NIL

获取哈希表键值对的数量:hash-table-count

  1. CL-USER> (defparameter *my-hash* (make-hash-table))
  2. *MY-HASH*
  3. CL-USER> (hash-table-count *my-hash*)
  4. 0
  5. CL-USER> (setf (gethash 'first *my-hash*) 1)
  6. 1
  7. CL-USER> (setf (gethash 'second *my-hash*) 2)
  8. 2
  9. CL-USER> (setf (gethash 'third *my-hash*) 3)
  10. 3
  11. CL-USER> (hash-table-count *my-hash*)
  12. 3
  13. CL-USER> (setf (gethash 'second *my-hash*) 'two)
  14. TWO
  15. CL-USER> (hash-table-count *my-hash*)
  16. 3
  17. CL-USER> (clrhash *my-hash*)
  18. #<EQL hash table, 0 entries {48205F35}>
  19. CL-USER> (hash-table-count *my-hash*)
  20. 0

性能

  1. CL-USER> (defparameter *my-hash* (make-hash-table))
  2. *MY-HASH*
  3. CL-USER> (hash-table-size *my-hash*)
  4. 65
  5. CL-USER> (hash-table-rehash-size *my-hash*)
  6. 1.5
  7. CL-USER> (time (dotimes (n 100000) (setf (gethash n *my-hash*) n)))
  8. Compiling LAMBDA NIL:
  9. Compiling Top-Level Form:
  10. Evaluation took:
  11. 0.27 seconds of real time
  12. 0.25 seconds of user run time
  13. 0.02 seconds of system run time
  14. 0 page faults and
  15. 8754768 bytes consed.
  16. NIL
  17. CL-USER> (time (dotimes (n 100000) (setf (gethash n *my-hash*) n)))
  18. Compiling LAMBDA NIL:
  19. Compiling Top-Level Form:
  20. Evaluation took:
  21. 0.05 seconds of real time
  22. 0.05 seconds of user run time
  23. 0.0 seconds of system run time
  24. 0 page faults and
  25. 0 bytes consed.
  26. NIL
  1. CL-USER> (log (/ 100000 65) 1.5)
  2. 18.099062
  3. CL-USER> (let ((size 65)) (dotimes (n 20) (print (list n size)) (setq size (* 1.5 size))))
  4. (0 65)
  5. (1 97.5)
  6. (2 146.25)
  7. (3 219.375)
  8. (4 329.0625)
  9. (5 493.59375)
  10. (6 740.3906)
  11. (7 1110.5859)
  12. (8 1665.8789)
  13. (9 2498.8184)
  14. (10 3748.2275)
  15. (11 5622.3413)
  16. (12 8433.512)
  17. (13 12650.268)
  18. (14 18975.402)
  19. (15 28463.104)
  20. (16 42694.656)
  21. (17 64041.984)
  22. (18 96062.98)
  23. (19 144094.47)
  24. NIL
  1. CL-USER> (defparameter *my-hash* (make-hash-table :size 100000))
  2. *MY-HASH*
  3. CL-USER> (hash-table-size *my-hash*)
  4. 100000
  5. CL-USER> (time (dotimes (n 100000) (setf (gethash n *my-hash*) n)))
  6. Compiling LAMBDA NIL:
  7. Compiling Top-Level Form:
  8. Evaluation took:
  9. 0.04 seconds of real time
  10. 0.04 seconds of user run time
  11. 0.0 seconds of system run time
  12. 0 page faults and
  13. 0 bytes consed.
  14. NIL
  1. CL-USER> (defparameter *my-hash* (make-hash-table :rehash-size 100000))
  2. *MY-HASH*
  3. CL-USER> (hash-table-size *my-hash*)
  4. 65
  5. CL-USER> (hash-table-rehash-size *my-hash*)
  6. 100000
  7. CL-USER> (time (dotimes (n 100000) (setf (gethash n *my-hash*) n)))
  8. Compiling LAMBDA NIL:
  9. Compiling Top-Level Form:
  10. Evaluation took:
  11. 0.07 seconds of real time
  12. 0.05 seconds of user run time
  13. 0.01 seconds of system run time
  14. 0 page faults and
  15. 2001360 bytes consed.
  16. NIL

Alist

Alist: An association list is a list of cons cells.

  1. (defparameter *my-alist* (list (cons 'foo "foo")
  2. (cons 'bar "bar")))
  3. ;; => ((FOO . "foo") (BAR . "bar"))

其结构如下:

  1. [o|o]---[o|/]
  2. | |
  3. | [o|o]---"bar"
  4. | |
  5. | BAR
  6. |
  7. [o|o]---"foo"
  8. |
  9. FOO

所以,通过其结构来对其进行定义:

  1. (setf *my-alist* '((:foo . "foo")
  2. (: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.

  1. (defparameter my-plist (list 'foo "foo" 'bar "bar"))
  1. [o|o]---[o|o]---[o|o]---[o|/]
  2. | | | |
  3. FOO "foo" BAR "bar"
  1. (defparameter my-plist (list 'foo "foo" 'bar "bar"))
  2. ;; => (FOO "foo" BAR "bar")
  3. (setf (getf my-plist 'foo) "foo!!!")
  4. ;; => "foo!!!"

结构体(Structres)

创建:defstruct

  1. (defstruct person
  2. id name age)

设置默认值

  1. (defstruct person
  2. id
  3. (name "john doe")
  4. age)

设置默认类型

  1. (defstruct person
  2. id
  3. (name "john doe" :type string)
  4. age)
  1. (defparameter *me* (make-person))
  2. *me*
  3. #S(PERSON :ID NIL :NAME "john doe" :AGE NIL)

当类型设置错误时:

  1. (defparameter *bad-name* (make-person :name 123))
  1. Invalid initialization argument:
  2. :NAME
  3. in call for class #<STRUCTURE-CLASS PERSON>.
  4. [Condition of type SB-PCL::INITARG-ERROR]

不使用关键次参数来创建结构体

  1. (defstruct (person (:constructor create-person (id name age)))
  2. id
  3. name
  4. age)
  1. (create-person 1 "me" 7)
  2. #S(PERSON :ID 1 :NAME "me" :AGE 7)

然而,默认的 make-person 不可以使用

  1. (make-person :name "me")
  2. ;; debugger:
  3. obsolete structure error for a structure of type PERSON
  4. [Condition of type SB-PCL::OBSOLETE-STRUCTURE]

访问 槽(slot)

  1. (person-name *me*)
  2. ;; "john doe"
  1. (setf (person-name *me*) "Cookbook author")
  2. (person-name *me*)
  3. ;; "Cookbook author"

断言

在创建结构时,一个断言函数会自动创建

  1. (person-p *me*)
  2. T

单继承

  1. (defstruct (female (:include person))
  2. (gender "female" :type string))
  3. (make-female :name "Lilie")
  4. ;; #S(FEMALE :ID NIL :NAME "Lilie" :AGE NIL :GENDER "female")

局限

修改后,实例不会被更新

  1. (defstruct person
  2. id
  3. (name "john doe" :type string)
  4. age
  5. email)
  6. attempt to redefine the STRUCTURE-OBJECT class PERSON
  7. incompatibly with the current definition
  8. [Condition of type SIMPLE-ERROR]
  9. Restarts:
  10. 0: [CONTINUE] Use the new definition of PERSON, invalidating already-loaded code and instances.
  11. 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.
  12. 2: [CLOBBER-IT] (deprecated synonym for RECKLESSLY-CONTINUE)
  13. 3: [RETRY] Retry SLIME REPL evaluation request.
  14. 4: [*ABORT] Return to SLIME's top level.
  15. 5: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING {1002A0FFA3}>)

如果重新开始的话,使用新的定义,将无法访问 *me*

  1. *me*
  2. obsolete structure error for a structure of type PERSON
  3. [Condition of type SB-PCL::OBSOLETE-STRUCTURE]

tree-equal copy-tree