append/cons car/cdr reduce

理解列表的关键是理解点对单元,虽然只要是点对单元都需要用括号引用起来,但是我们平常说的list是特指那些后面的位置我们不直接存字面值了,改为地址,比如下一个点对的引用,所以当CDR为后续点对的链接或NIL时,Lisp打印器可以理解这种约定并能将这种链状的点对单元打印成括号列表而不用用点分隔的点对。注意第二个与第四个的形式区别与原因。

一:append/cons

cons       (cons se1 se2 ) Return a list with SE1 as the CAR and SE2 as the CDR. 它是一个二元函数

append  (append &rest lists)  Construct a new list by concatenating the list arguments

cons会首先建一个节点,如果第一个参数为列表的话,他就用新建节点的第一个位置指向这个列表,如果第一个参数为字面值的话,他会让新建节点的值为这个字面值,也就是不论如何cons会先建立一个节点。然后再看第二个参数,如果是数字的话,就让数字去填充这个新建节点的第二个位置,注意他不会去补下面3后面的nil,cons会维持前面列表的结构,这就是为啥有一个列表就要引入一个新的节点的原因。而如果第二个参数是一个列表的话,他会让新建列表的第二个位置指向它,而不是再建一个节点来指向它,可以看下面的代码6的返回形式。

CL-USER> (setf test1 (cons '(1 3) 4))   ((1 3) . 4);;见图一
CL-USER> (cdr test1)                     4         ;;说明并不是说只有car才能够取到值,凡是取到的结果不是点对结构就行。
CL-USER> (car test1)                    (1 3)
CL-USER> (cons 4 '(2 3))                (4 2 3)    ;;见图三,4本身所在的就是新建节点,所以用第二个位置指向了第二个参数列表,但是巧合的是它最后输出形式是合并到一块了,因为他这个是误打误撞构成了lisp中认可的list形式。
CL-USER> (cons 3 4)                     (3 . 4)
CL-USER> (cons '(2 3) '(6 0))          ((2 3) 6 0)

                                       

append字面意思就是挂到别人后面,所以说现在现在如果你对两个list调用append方法,它们会合并到一个list里面。并且如果第二个参数是数字的话,它会把这个数字加入到第一个参数的nil中,如果第一个参数是一个点对结构结尾的话,就会报错,也就是说对于append的话,第一个参数必须是一个有一个nil位置,用于指向第二个参数(对象)或则用于让第二个参数替换(字面值),这也就是为啥append后面第一个参数必须是list的原因,注意我这里写的list是本文第一行中所述符合common lisp 规则的list.

CL-USER> (setf test (append '(1 3) 4))      (1 3 . 4)
CL-USER> (cdr test)                         (3 . 4)
CL-USER> (car test)                          1      
CL-USER> (cdr (cdr test))                    4   ;;关于为啥cdr我们也得到了一个值,而不是一个列表呢? 下面有讨论
CL-USER> (append (cons 5 6) '(5))            no value  ;;这个报错说6 is not a list. 因为我门从(cons 5 6)得到的是一个点对

结构,没有地方可以放后面的参数了。总而言之,append的第一个参数要给第二个参数留有余地。也就是必须为list.
CL> (append '( 1 2) '( 3 5))                (1 2 3 5)    见图六
CL> (append (list '(1 3)) (list 6 7))       ((1 3) 6 7)  见图七,你可以看出他们是在toplevel进行追加的。

二:cdr/car 

你会发现前面的例子中cdr最后也会返回一个整数而不是一个list. 其实这有个误解就是凡是car都是抽取元素,而cdr是渗入列表并且返回一个list. 其实只有上面我给的点对结构才能用一个括号给括起来。本身cdr并没有说会不论你什么结构都给你加一个括号。

CL-USER> (setf test2 (cons 4 (cons 3 4)))    (4  3 . 4)
CL-USER> (car test2)                          4
CL-USER> (cddr test2)                         4           ;;因为最后只剩下了一个4,他不是点对结构所以返回一个整数。
CL-USER> (cdr test2)                         (3 . 4)      ;;因为是一个点对结构,所以返回括号引用
                                     

看下面一个例子,为啥cdr又给列表加了一个括号?先看图五他们的结构形式 

CL-USER> (defvar *uu* '(1 (2 3) (4 5)))    *UU*  
CL-USER> (cdr (cdr *uu*))                  ((4 5))  ;;其实这个也就是我在上面说明要注意的地方,这个他隐含着有个节点连接着这个list,并且节点的第二个值为nil.你可以从第四行代码返回nil得到。
CL-USER> (car (cdr (cdr *UU*)))            (4 5)  
CL-USER> (cdr (cdr (cdr *UU*)))            NIL  


三:用reduce函数来define copy-list

这个是reduce的具体执行方式,

有initial-value时,如果from-end为true,意思就是最后面的放到第一个参数位置,并且执行后的结果仍旧是放到第二个位置;列表中的元素都是作为第一个参数。如果from-end不为true,列表中的元素都是作为第二个参数。

当没有initial-value时要注意from-end时,跟前面一样,列表中的所有元素都是作为第一个参数,但是第一次的时候注意是倒数第二个参数在前面。


CL-USER> (defun reduce-copy-list(list)
	   (setf p (reduce #'cons list :from-end t :initial-value ())))
REDUCE-COPY-LIST
CL-USER> (cons 3 ())                            (3)
CL-USER> (cons 2 '(3))                          (2 3)
CL-USER> (cons 1 (cons 2 (cons 3 ())))          (1 2 3)
CL-USER> (setf p (copy-list '(1 3 4)))          (1 3 4)
CL-USER> (reduce-copy-list p)                   (1 3 4)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值