列表

1:点对的概念

理解列表的话,先清楚所谓的点对单元如下图,其中a,b可以为任何对象。Cons 接受两个实参并且返回他们两个组成的点对单元。CAR是该列表的第一个元素,CDR则包含着其余元素。既然a,b可以为任何对象,如果串过去的是两个整数实参也就是如下(1.2)的点对形式,

但是这样的结构不能够再继续扩展。所以我们就引入了Lisp中的列表规则:后面的位置我们不直接存字面值了,改为地址,比如下一个点对的引用,所以当CDR为后续点对的链接或NIL时,Lisp打印器可以理解这种约定并能将这种链状的点对单元打印成括号列表而不用用点分隔的点对。

CL-USER> (cons 1 2)
(1 . 2)

2Lisp 中约定的列表形式

(1 2 3)这个列表实际上就是3点对单元链接组成,具体结构如下:


列表时通过将点对以链状连接在一起而构成的。如上图所示列表的元素被保存在点对的CAR中,对后续点对的链接则被保存在CDR,链上最后一个单元的CDRNIL

代码实现如下,正好符合lisp约定,列表中的CDR是另一个列表的引用,最后一个为NIL.

CL-USER> (cons 1 (cons 2 (cons 3 nil)))
(1 2 3)

平常我们使用列表时候并不需要直接处理点对,用Cons函数来组建列表,而是用List函数,下面代码与上面CONS代码等价。

CL-USER> (list 1 2 3)

(1 2 3)

因为点对单元可以接受任何对象,同理lisp中的点对单元的第一个元素也可以指向函数或者列表。

(list  "foo"  (list 1 2)  10) ---> ("foo"  (1 2)  10)

列表_第1张图片

3:函数式编程和列表

函数式编程本质在于,程序完全由没有副作用的函数组成,也就是说,函数完全基于其参数的值来计算结果。并且由于函数的结果仅取决于其参数的值,因此易于测试和理解。

因为Lisp是半函数式编程,所以就会有一些情况会产生副作用(除了进行参数和返回值外,还进行其他方面的数据交互)

Append 会返回一个列表,首先复制12两个点对单元然后将他们连接起来并且指向点对的CDR 3,4,也就是现在他们与(3 4)共享了一些点对单元。如下面所示,当你更改新列表的时候,有可能会对老的列表进行了修改。Append被指定总是返回以特定方式共享结构的结果。

列表_第2张图片

CL-USER> (defvar *test* '(1 2))                 *TEST*
CL-USER> (defvar *test1* '(3 4))                *TEST1*
CL-USER> (defvar *m* (append *test* *test1*))   *M*
CL-USER> (setf (car *m*) 7)                     7
CL-USER> *test*                                 (1 2)
CL-USER> *test1*                                (3 4)
CL-USER> (setf (car (cdr (cdr *m*))) 12)        12
CL-USER> *m*                                    (7 2 12 4)
CL-USER> *test*                                 (1 2)
CL-USER> *test1*                                (12 4)

4:破坏性操作。

上面已经说了lisp不是纯函数式编程,所以就会有一些更改原有对象的情况,凡是对原对象有更改的操作我们都称为破坏性操作(destructive),一般情况下我们都称之为副作用,但是现在加以细化又有一个回收性操作。

上面append的操作就是副作用操作。回收性操作既然也是对原对象有修改,顾名思义就为原资源的重新利用,它实际上也是一种资源的优化。所以说只有当调用了回收性函数之后不再需要原先列表的情况下,回收性函数才能够安全的使用。

很多函数都有非破坏函数和回收性函数,他们一般名字相同除了回收性函数对了一个前缀N含义是non-consing. 表示不需要分配任何点对单元。Cons是构建点对单元时的函数名字。

Reverse 本身是返回一个序列的,其回收性为NCONC

CL-USER> (defvar *aa* '(1 2))                         *AA*
CL-USER> (defvar *bb* '(3 4))                         *BB*
CL-USER> (defvar *pp* (nconc *aa* *bb*))              *PP*
CL-USER> *pp*                                         (1 2 3 4)
CL-USER> (setf (car *pp*) 5)                          5
CL-USER> *aa*                                         (5 2 3 4)
Nsubstitute和它的-if 和-if-not 变体,是substitute的回收性。
CL-USER> (defvar *foo* "football")                   *FOO*
CL-USER> (substitute #\n #\o *foo*)                  "fnntball"
CL-USER> *foo*                                       "football"
CL-USER> (nsubstitute #\n #\o *foo*)                 "fnntball"
CL-USER> *foo*                                       "fnntball"

对于delete/delete-if/delete-duplicates有时为回收性,有时不是。比方说如果原序列的头或尾被删除时,一般都是非回收性。如果的话可以用(setf  *list* (delete *list*)),

综上如果性能评估说要优化,你可以将非破坏性替换为相应的回收性操作,前提是必须确定其他任何位置不会引用实参列表时才可以这么做。

5:列表处理

可以first/car(用于抽取元素)和rest/cdr(用于深入列表)

Nth    后跟 索引和列表,如果不含内嵌列表就返回第个元素,他跟Nthcdr的效率不会高于明确给出car/cdr的实现,因为你不能执行cdr就得到第n个元素。

Nthcdr 返回调用cdr n次的结果。

最多4AD组成的序列放到CR之间,A代表CARD代表CDR

(cadadr list)==(car (cdr (car (cdr list))))

最容易出错的就是内嵌一个列表,最容易出错的是cdddr这一步返回了一个nil.当你遇到内列表时,把他先看成一个点对单元对待,就可以理解为啥((4 5))CDRnil,注意下面他的结构,如果是(1 2 3)形式的话,cadr就可以得到第二个元素了,现在是(2 3),也就是本来应该是个值的,现在成为了另一个列表的引用。同理当caddr应该得到第三个元素的值,现在因为第3个位置是一个列表所以就得到了一个列表的引用。

CL-USER> (defvar *uu* '(1 (2 3) (4 5)))    *UU*
CL-USER> (cdr (cdr *uu*))                  ((4 5))
CL-USER> (car (cdr (cdr *UU*)))            (4 5)
CL-USER> (cdr (cdr (cdr *UU*)))            NIL
列表_第3张图片

注意点:内列表的书写方式。

(list 1 (list 2 3) (list 4 5) 都调用list函数

'(1 (2 3) (4 5))              因为外层是一个引用,所以本身就是值了。

(list 1 '(2 3) '(4 5))        引用的形式不变,然后再用list函数处理

'(1 '(2 3) '(4 5))            现在'也相当于列表中的元素,因为最外层是引用,表达式的结果保持原样。

CL-USER> (cdr '('(1 2) '(3 4) '(5 6)))              ('(3 4) '(5 6))
CL-USER> (caadr '('(1 2) '(3 4) '(5 6)))            QUOTE
CL-USER> (cadr '('(1 2) '(3 4) '(5 6)))             '(3 4)
CL-USER> (cdadr '('(1 2) '(3 4) '(5 6)))            ((3 4))
CL-USER>  (cadadr '('(1 2) '(3 4) '(5 6)))           (3 4)
CL-USER>  (caadadr '('(1 2) '(3 4) '(5 6)))
; No value
CL-USER>  (car (car (cdr (car (cdr '('(1 2) '(3 4) '(5 6)))))))
3



CL-USER> (defvar *ff* '(1 2 4))
*FF*
CL-USER> (defvar *kk* '(10 20 30))
*KK*
CL-USER> (mapcar #'+ *ff* *kk*)
(11 22 34)
CL-USER> (mapcan #'+ *ff* *kk*)
34

你可能感兴趣的:(编程,优化,list,delete,扩展,lisp)