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)

用直观的图形来表示:
原子状态:
[img]http://www.gigamonkeys.com/book/figures/one-cons-cell.png[/img]
模拟个(1 2 3)列表:
[img]http://www.gigamonkeys.com/book/figures/list-1-2-3.png[/img]

当当当...隆重引出我们的列表专用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)

看起来就像这个样子:
[img]http://www.gigamonkeys.com/book/figures/mixed-list.png[/img]
原则上,如果需要,列表可以无限扩展任意深度。

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

直观图:
[img]http://www.gigamonkeys.com/book/figures/after-append.png[/img]
(这图不错啊~)
细心的人可能已经发现,似乎并非是一个全新的列表啊,被你发现了~

(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

目前只要知道这些表达式的意思就可以了,最好别多用~
以下是一些基本的不完全的功能列表:
[table]
|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.
[/table]

作为一种数据结构,可遍历也是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有好的代码风格,做到数据传递不被破坏(或者是预期的破坏),一切就都是浮云了。

(未完待续)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值