没有列表

; 没有列表
; 列表是构建在点对单元之上的描述,使用cons创建点对单元
(cons 1 2) ;(1 . 2)
; 点对单元的两个值分别叫car和cdr,同时又是访问这两这个值的函数名,
; 并且支持setf赋值
(car (cons 1 2));1
(cdr (cons 1 2));2
(defparameter *cons* (cons 1 2))
(setf (car *cons*) 10);10
*cons*;(10 . 2)
; 点对中的值可以使任何对象的引用,点对连接在一起可以构成更大的结构
; 列表时点对链式连接起来的,
; 列表元素保存在car中,后续点对的连接保存在cdr中,
; 最后一个单元的cdr是nil,同时代表空列表和元素nil
; 单链表,我们说一个值时列表,要么是nil要么是对某个点对单元的引用
; 该点对单元的car是列表第一个元素,
; cdr是其余元素,引用这其他的列表,可能是nil或者另外的点对单元
(cons 1 nil);(1)
(cons 1 (cons 2 nil));(1 2)
(cons 1 (cons 2 (cons 3 nil))) ;(1 2 3)
; ┌─┬─┐
; └─┴─┘左边car右边cdr,表示一个适当的值要么画在框内,要么用箭头引用
; ┌──┬──┐
; │1 │ │
; └──┴──┘
; ┌──┬──┐ ┌──┬──┐ ┌───┬───┐
; │1 │ ────2 │ ──── 3 │nil│
; └──┴──┘ └──┴──┘ └───┴───┘
; list创建列表的函数,会为你自动做这一切
(list 1)
(list 1 2)
(list 1 2 3)
; 从列表的角度考虑问题,可以使用first,rest代替car,cdr
(first (list 1 2 3));1
(first (rest (list 1 2 3)));2
; 点对单元可以保持任意对象,所以列表页能保存任何的值
(list "foo" (list 1 2) 3);("foo" (1 2) 10)
; 图示
; ┌──┬──┐ ┌──┬──┐ ┌───┬───┐
; │ │ ──── │ ────10 │nil│
; └│─┴──┘ └─│┴──┘ └───┴───┘
; │ │
; "foo" │
; ┌──┬──┐ ┌───┬───┐
; │1 │ ──── 2 │nil│
; └──┴──┘ └───┴───┘
 
 
;;;;;;; 函数式编程和列表
; 函数式编程的本质,程序由完全没有副作用的函数组成,完全依赖于参数的只计算结果
; 处理数字的函数天生是函数式的,因为数字是不可改变的对象
; 列表是可以改变的,可以setf改变,
; 如果将列表的值视为由他们包含元素决定,
; 那么(1 2 3 4)等价于任何包含着4个值的列表,无论其对应什么点对单元
; 任何接受一个列表作为实参,并且返回值完全依赖于此列表的函数,也可以看做函数式
; 多数列表函数是用函数式写的,这是因为这能使他们返回与其实参共享点对单元的结果
(append (list 1 2) (list 3 4));(1 2 3 4)
; 实际上市复制了(list 1 2)点对单元,将2后面的nil指向了(list 3 4)
; append的结果总是共享最后一个实参
; cl不是纯函数式的,修改已有对象的操作被认为是破坏性的。
; 修改一个对象的状态,就是破坏了它,已经不是相同那个对象了。
 
;;; 修改的分类:副作用性操作 回收性操作
; 副作用性操作:专门利用其副作用修改已有对象的状态,setf和底层使用setf的VECTOR-PUSH,VECTOR-POP等
; cl是多范式的,函数式风格代码慎用非函数式的副作用操作
; 事例
(defparameter *list-1* (list 1 2))
(defparameter *list-2* (list 3 4))
(defparameter *list-3* (append *list-1* *list-2*)) 
 
(setf (first *list-2*) 0)
; *list-2*变成 (0 4),副作用*list-3*变成(1 2 0 4),杯具啊
 
;回收性操作
; 本来就是用在函数式代码中的,它的副作用是一种优化手段
; 他们在构造结果时,会重用实参中特定的点对单元
; 和append不同的是,回收性函数将点对单据作为原材料用,
; 如有必要它将修改car,cdr来构造结果
; 只有当调用回收性函数之后不需要原先列表的时候,才可安全使用
 
(setf *list* (reverse *list*))
; reverse不修改其参数,
; 返回结果是为逆序的列表重新生成点对单元
; 赋值的过程丢失了对原有*list*的引用,原有*list*被垃圾回收
 
; 【立即直接重用已有的点对单元】比【新分配单元,垃圾回收老单元】高效
; nreverse就这样做,n的意思是不分配新的点对单元
; 一般n开头的函数,页对应一个没有破坏性的函数。有几个例外
; NCONC是append的回收性版本
; DELETE DELETE-IF DELETE-IF-NOT DELETE-DUPLICATES是序列函数remove家族的回收性版本
; 多数回收性函数的副作用并未严格到足以信赖的程度,
; 所以回收性函数的使用方式一般也如上使用
(setf *list* (nreverse *list*))
; 某些函数具有明确的副作用
; NCONC
; NSUBSTITUTE ,NSUBSTITUTE-IF NSUBSTITUTE-IF-NOT
 
; NCONC构造过程:
; 只要参数是非空列表,
; 前一个列表的最后一个点对单元的cdr,设置成后一个列表第一个点对单元的car
(defparameter *list* (list 1 2))
(nconc *list* (list 3 4));(1 2 3 4)
*list*;(1 2 3 4)
; NSUBSTITUTE替换过程,沿着列表遍历,替换点对的car
; 不依赖回收性函数的副作用,只是用其返回值来赋值是一种良好的编程风格
 
; ;;组合回收性函数和共享结构
; 使用共享结构是不在乎列表由哪些点对构成
; 使用回收函数式明确知道参数不会引用到
; 回收性函数的习惯用法:
; 1构造列表
(defun upto (max)
    (let ((result nil))
        (dotimes (i max)
            (push i result))
        (nreverse result)))
(upto 5);(0 1 2 3 4)
;push是向列表头放,reverse过来正好
; result在函数体内,其他地方不会引用到,可以用回收性函数
 
; 2回收性函数的返回值重新赋值到可能被回收的位置上
(setf foo (delete nil foo))
; 仍然需要小心使用,foo如果共享结构则会副作用
; 前面的例子
; *list-2* (0 4)
; *list-3* (1 2 0 4)
(setf *list-3* (delete 4 *list-3*));(1 2 0)
; *list-3*第三个元素的cdr变成nil,*list-2*变成了(0)
 
; 以上两种习惯用法push/nreverse,setf/delete占据了回收函数80%的应用
; 编程风格,操作列表时首先用函数式编程方式写代码,遇到性能瓶颈再改
; SORT STABLE-SORT MERGE操作列表时是回收性函数,没有破坏性同伴
; 这个时候先把实参copy-list一下,用于sort
 
; 列表处理函数库
; REST ;深入列表
; FIRST ;抽取元素,以上两者组合可以获取任意元素
; SECOND---TENTH都可以使用,最常用的是(NTH 索引 列表)
; NTHCDR返回列表调用N次cdr的结果,rest == (nthcdr 1 列表)
; 28个复合car/cdr函数,cr之间最多4个,现在不大常用了
; (caar list) ====(car (car list))
; (cadadr list) ===(car (cdr (car (cdr list))))
; 如果非函数式使用列表FIRST - TENTH CAR CDR 可作为setf的位置
; (last *list* n)返回最后n个点对单元,n默认值1
; (butlast *list* n)返回排除最后n个点对,n默认1
; (nbutlast *list* n)
; (tailp *list-1* *list-3*);nil
; (tailp *list-2* *list-3*);t,如前面例子,前面对象是后面列表的一部分的点对单元返回真
; (ldiff *list-1* *list-3*);(1 2)返回列表致悼某个给定点对单元的副本
; (list* 1 2 '(3 4));(1 2 3 4)
; list*组合了list和append,用除最后一个参数的所有参数构造一个列表,
; 然后将这个最后一个元素的cdr赋成最后一个参数
; (make-list n :initial-element nil)跟前面(make-array )差不多
; (revappend list1 list2) list1 reverse后执行(append list1 list2)
; (NRECONC list1 list2); 以上函数的回收性版本
; (CONSP A)测试一个对象是否为点对单元的谓词
; (atom a)测试一个对象不是点对单元的谓词
; (listp a)测试一个对象是否是点对单元或者nil的谓词
; (null a)测试一个对象是否为nil的谓词
 
; ;;;;映射
; 函数式编程之高阶函数,函数可以作为参数和返回值
; 6个特别用于列表上的映射函数
; mapcar返回列表,参数是函数和列表
(mapcar #'(lambda (x) (* 2 x)) (list 1 2 3)) ;(2 4 6)
(mapcar #'+ (list 1 2 3) (list 4 5 6) (list 7 8 9))
; maplist返回列表,参数是函数和点对单元
; 能访问到列表中的car cdr
; mapcar,maplist会构造新的列表
; mapcan,mapcon 
; 将结果列表nconc连接起来,每次函数调用都可以向结果列表提供任意数量元素
; mapcan跟mapcar一样传递列表到函数中
; mapcon跟maplist一样传递点对单元到函数中
; mapc,mapl是控制构造,只返回第一个列表实参,当映射函数有副作用,他们才有用
 
 
 
 
 




posted @ 2012-04-13 11:05  舜耕山翁  阅读(164)  评论(0编辑  收藏  举报