common Lisp学习笔记(十三)

lisp_chapter13

13 Arrays, Hash Tables, and Proterty Lists

13.2 array

数组是内存中的一些连续的存储空间,一维的数组也叫做vector,第一个元素下标也是从0开始

注意数组与list的区别,list相当于链表,每个元素需要一个cons相当于两个空间,一个指向内容一个指向下一个 地址,而数组中每个元素只需要一个空间,每个数组前面有一个header,里面的信息有数组的长度,维数等。数组 的元素访问速度要快于list,因为可以直接通过下标计算地址来进行访问,而链表越往后的元素访问速度就越慢。 相比之下,list的优势是容易创建,可以通过递归或者迭代的方式创建,而且可以动态扩建,还有就是list可以使用 共享的cons,这也是数组不能实现的

  • 创建数组: (setf my-vec '#(tuning violin hello 11)),与list的区别是多了一个#
  • 输出

lisp中有一个全局变量*print-array*,如果该变量是t,则输出数组时可以看到每一个元素,如果置为nil,则输出 #<>的缩略形式

> (setf *print-array* nil)
nil

> my-vec
#<vector {204844}>

> (setf *print-array* t)
t

> my-vec
#(tuning violin hello 11)
  • access elements

aref可以访问数组的元素,类似于list中nth的用法

> (aref my-vec 1)
violin

> (setf (aref my-vec 1) 'piano)
piano

一些函数是数组和list都可以用的,如length, reverse, find-if等

另一些函数只能用于list,如car/cdr, member, 和其他集合函数等

13.5 make-array

make-array创建并返回一个新的数组,如(make-array 5)创建长度为5的数组。如果不声明元素的初始值,可能为0 也可能为nil

:initial-element关键字是数组元素的初始值,:initial-contents则是一个用来初始化数组的list,长度必须 和数组的长度一样

> (make-array 5 :initial-element 1)
#(1 1 1 1 1)

> (make-array 5 :initial-contents '(a e i o u))
#(a e i o u)

13.6 string as vectors

字符串是一种特殊的vector,对数组可以使用的函数都可以对string使用

(length "hello") -> 5
(reverse "hello") -> "olleh"
(aref "hello" 1) -> #\e

注意string的每个元素例如#\e,都是char类型,字符类型不需要使用',因为它们的值都是自身

#\k -> #\k
(type-of #\k) -> standard-char

因为字符串里每个元素都是字符类型,所以用setf对元素赋值的时候只能用char类型,否则会报错

13.7 hash tables

哈希表功能类似association list,但是访问速度比较快,因为哈希表是用数组实现的

  • 哈希表只能通过make-hash-table函数来创建
    > (setf h (make-hash-table))
    #<eql hash table ...>
    
  • (gethash key hash-table): gethash函数返回key的哈希值,可以用setf对其赋值
    > (setf (gethash 'john h) '(hello world))
    (hello world)
    > (setf (gethash 'mary h) '(good luck))
    (good luck)
    
    > (gethash 'john h)
    (hello world)
    t
    

gethash函数返回两个值,第一个是哈希值,如果key不存在则返回nil。第二个值为t或者nil, 表示key存在或者不存在。这种设计是因为有可能某个key的哈希值本身就是nil,这样就无法判断这个key是否存在

  • (describe h): 返回哈希表的一些有用信息
    > (describe h)
    #<eql hash table ...> is a hash-table
    it currently has 2 entries and 65 buckets.
    

13.8 priority list

在lisp中,每个symbol都有一个priority list. priority list可以看作是一系列的键值对,每个key称为indicator, priority list形如(indicator1 value1 indicator2 value2 ...)

priority list一般在比较旧的lisp中使用,现在可以用association list或者hash table来代替

  • (get symbol indicator [default]): 获取某个indicator对应的值
    (setf (get 'fred 'sex) 'male)
    (setf (get 'fred 'age) 23)
    
    > (describe 'fred)
    fred is a symbol
    its age property is 23
    its sex property is male
    

get函数是用eq来对key进行比较的,所以indicator不能是数字

get函数还可以有第三个参数,即如果key找不到时的缺省值,可以通过提供第三个参数来区别一个indicator不存在 或者是存在但值为nil的情况,因为这两种情况都是返回nil

(setf (get 'mabel 'siblings) nil)
(get 'mabel 'siblings 'unknown) -> nil
(get 'clare 'siblings 'unknown) -> unknown
  • (symbol-plist symbol): 返回符号的property list
    > (symbol-plist 'fred)
    (age 23 sex male)
    
  • (remprop symbol indicator): remove property, 删除某个键值对
    (remprop 'fred 'age)
    (get 'fred 'age) -> nil
    

ex 13.8

(setf *hist-array* nil)
(setf *total-points* 0)

(defun new-histogram (num-bins)
  (setf *total-points* 0)
  (setf *hist-array* (make-array num-bins :initial-element 0))
  t)

(defun record-value (n)
  (incf *total-points*)
  (if (and (>= n 0) (< n (length *hist-array*)))
    (incf (aref *hist-array* n))
    (error "value ~S out of bounds." n)))

(defun print-hist-line (n)
  (format t "~&~2S [~3S] " n (aref *hist-array* n))
  (dotimes (i (aref *hist-array* n))
    (format t "*")))

(defun print-histogram ()
  (dotimes (i (length *hist-array*))
    (print-hist-line i))
  (format t "~&    ~3D total" *total-points*))

> (new-histogram 11)
t
> (dotimes (i 200) (record-value (random 11)))
nil
> (print-histogram)

lisp toolkit: room

lisp有几种不同的方法来获取更多的内存,一种是重新声明一些以前用过但不再使用的空间,即垃圾回收,还有一种 是让操作系统分配更多的内存

(gc) 函数可以让lisp马上开始垃圾回收

(room) 函数显示当前的内存使用情况

13.11 coerce

coerce函数可以将一列元素转化为另一种类型,如list类型转为string类型

> (coerce "cookie" 'list)
(#\c #\o #\o #\i #\e)
> (coerce '(#\h #\i) 'string)
"hi"
> (coerce '(foo bar baz) 'vector)
#(foo bar baz)

创建一个string的另一个方法是用make-array, 用关键字:element-type设定元素类型为string-char

> (make-array 3 :element-type 'string-char :initial-content '(#\m #\o #\m))
"mom"

对于大部分applicative operators,例如find-if, reduce, 都可以对任何种类的sequence使用,不局限于list。 但是mapcar只能用于list, map可以用于任何sequence,与mapcar的区别是第一个参数是返回的序列的类型

> (map 'list #'+ '(1 2 3 4) '(10 20 30 40))
(11 22 33 44)

如果map的第二个参数是nil,则返回nil。比如我们只需要输出后面的序列而不需要返回序列的任何内容时可以使用

posted @ 2015-06-01 21:38  Jolin123  阅读(377)  评论(0编辑  收藏  举报