lisp初体验-Practical Common Lisp笔记-14.因为list,它被称为Lisp

上一章讲了向量、哈希表等比Lisp中较常用的数据结构。就如今的Common Lisp而言,的确选择很多,而过去却只有列表(list)这一个选项,so~

当然历史原因只是其一(话说能在历史中被选择的总有其合理之处),就当前通常可见的场景下,List也是一个不错的选择,其实用性早已被时间所证明(有点像数学啊,很多公理都早已被确定~)。所以,了解List,知道选择List的优劣还是很有必要的。

跳过一段"花非花"的论断,让我们直接进入叫做"cons cells"的东东(算是list的原型吧,无需深入,现在都用新的定义方式了):

我们用cons来定义、创建这个结构:
(cons 1 2) ==> (1 . 2)

一些基本的动作:
(car (cons 1 2)) ==> 1
(cdr (cons 1 2)) ==> 2

(defparameter *cons* (cons 1 2))
*cons*                 ==> (1 . 2)
(setf (car *cons*) 10) ==> 10
*cons*                 ==> (10 . 2)
(setf (cdr *cons*) 20) ==> 20
*cons*                 ==> (10 . 20)

合并、嵌套:
(cons 1 nil)                   ==> (1)
(cons 1 (cons 2 nil))          ==> (1 2)
(cons 1 (cons 2 (cons 3 nil))) ==> (1 2 3)

用直观的图形来表示:
原子状态:

模拟个(1 2 3)列表:


当当当...隆重引出我们的列表专用key:List
(list 1)     ==> (1)
(list 1 2)   ==> (1 2)
(list 1 2 3) ==> (1 2 3)

对应的一些基本动作:
(defparameter *list* (list 1 2 3 4))
(first *list*)        ==> 1
(rest *list*)         ==> (2 3 4)
(first (rest *list*)) ==> 2

列表还能轻松hold住各种类型的元素:
(list "foo" (list 1 2) 10) ==> ("foo" (1 2) 10)

看起来就像这个样子:

原则上,如果需要,列表可以无限扩展任意深度。

关于列表,Lisp为其配套了一大堆功能性函数,在这儿就不一一阐述,取其典型:append
(append (list 1 2) (list 3 4)) ==> (1 2 3 4)

直观图:
lisp初体验-Practical Common Lisp笔记-14.因为list,它被称为Lisp
(这图不错啊~)
细心的人可能已经发现,似乎并非是一个全新的列表啊,被你发现了~
(defparameter *list-1* (list 1 2))
(defparameter *list-2* (list 3 4))
(defparameter *list-3* (append *list-1* *list-2*))

*list-1*                  ==> (1 2)
*list-2*                  ==> (3 4)
*list-3*                  ==> (1 2 3 4)

(setf (first *list-2*) 0) ==> 0
*list-2*                  ==> (0 4)     ; as expected
*list-3*                  ==> (1 2 0 4) ; maybe not what you wanted

似乎符合发现,但不符合预期啊。这种关联是有适用场景的,从某种程度上节约了数据空间的成本。所以,使用的时候需要小心。
其实大部分对列表的操作函数其实是不破坏原表的,除非你写成这个样子:
(setf *list* (reverse *list*))

生成新列表对于确保粒子的原子性有帮助,不过对于空间就比较糟蹋了,如果对空间有需求的话,可以使用类似上面的语句来覆盖原表,达到空间复用的目的:
(defparameter *x* (list 1 2 3))
(nconc *x* (list 4 5 6)) ==> (1 2 3 4 5 6)
*x* ==> (1 2 3 4 5 6)

下面来看一个关于循环和结构共享的例子:
(defun upto (max)
  (let ((result nil))
    (dotimes (i max)
      (push i result))
    (nreverse result)))

(upto 10) ==> (0 1 2 3 4 5 6 7 8 9)

上面的代码创建了独立无外部关联的列表。对于数据空间共享,还有个小窍门:
*list-2* ==> (0 4)
*list-3* ==> (1 2 0 4)

(setf *list-3* (delete 4 *list-3*)) ==> (1 2 0)
*list-2* ==> (0)

如果上面的delete改用remove的话就不会出现串联同步修改的问题了。

有无损的函数,自然就会有会损坏原表的,在这种情况下,如果有必要,copy-list是一个不错的选择:
CL-USER> (defparameter *list* (list 4 3 2 1))
*LIST*
CL-USER> (sort *list* #'<)
(1 2 3 4)                      ; looks good
CL-USER> *list*
(4)                            ; whoops!


因为一些历史原因,我们还可以看到一些比较奇怪的表达式:
(caar list) === (car (car list))
(cadr list) === (car (cdr list))
(cadadr list) === (car (cdr (car (cdr list))))

(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

目前只要知道这些表达式的意思就可以了,最好别多用~
以下是一些基本的不完全的功能列表:
Function Description
LAST Returns the last cons cell in a list. With an integer, argument returns the last n cons cells
BUTLAST Returns a copy of the list, excluding the last cons cell. With an integer argument, excludes the last n cells.
NBUTLAST The recycling version of BUTLAST; may modify and return the argument list but has no reliable side effects.
LDIFF Returns a copy of a list up to a given cons cell.
TAILP Returns true if a given object is a cons cell that's part of the structure of a list.
LIST* Builds a list to hold all but the last of its arguments and then makes the last argument the CDR of the last cell in the list. In other words, a cross between LIST and APPEND.
MAKE-LIST Builds an n item list. The initial elements of the list are NIL or the value specified with the :initial-element keyword argument.
REVAPPEND Combination of REVERSE and APPEND; reverses first argument as with REVERSE and then appends the second argument.
NRECONC Recycling version of REVAPPEND; reverses first argument as if by NREVERSE and then appends the second argument. No reliable side effects.
CONSP Predicate to test whether an object is a cons cell.
ATOM Predicate to test whether an object is not a cons cell.
LISTP Predicate to test whether an object is either a cons cell or NIL.
NULL Predicate to test whether an object is NIL. Functionally equivalent to NOT but stylistically preferable when testing for an empty list as opposed to boolean false.


作为一种数据结构,可遍历也是list的基本特性,所以map类函数也都有对应的解决方案:
MAPCAR
(mapcar #'(lambda (x) (* 2 x)) (list 1 2 3)) ==> (2 4 6)
(mapcar #'+ (list 1 2 3) (list 10 20 30)) ==> (11 22 33)

MAPLIST 对应cdr
MAPCAR/MAPLIST 生成新的list
MAPCAN/MAPCON  生成组合的list
MAPC/MAPL      结果覆盖第一个list

跳过了很多描述性和观点性的东西(有些是晦涩,有些是暂不认同),至于具体的函数,用到时看看help,文档应该很容易覆盖吧。
其实,就总体而言,只要coder有好的代码风格,做到数据传递不被破坏(或者是预期的破坏),一切就都是浮云了。

(未完待续)

你可能感兴趣的:(lisp)