27/ 为什么 Lisp 没有指针 (Why Lisp Has No Pointers)
一个理解 Lisp 的秘密之一是意识到变量是有值的,就像列表有元素一样。如同Cons对象有指针指向他们的元素,变量有指针指向他们的值。
;;可以把变量当成一个容器,可以装不同的东西。
28/ 函数copy-list接受一个列表,然后返回此列表的复本。新的列表会有同样的元素,但是装在新的Cons对象里:每个cons对象装在不同的容器中。
>(setf x '(a b c);;;定义变量x并赋值
y (copy-list x) );;;把容器x中得值,拷贝一份到容器y
(ABC);结果,返回最后运算值,此处为y的值
测验:
* (eql x y)
NIL
* (equal x y)
T
29/ 函数append返回任何数目的列表串接 (concatenation):它复制所有的参数,除了最后一个
* (append '(a b) '(c d) '(t y u) '(y i) '(5 6) '(c d) 'e) ;;最后一个不为列表形式,其余为列表
(A B C D T Y U Y I 5 6 C D . E)
* (append '(a b) '(c d) '(t y u) '(y i) '(5 6) '(c d) 'f);;;复制所有的参数,除了最后一个,若末位实参非列表,则?
(A B C D T Y U Y I 5 6 C D . F)
* (append '(a b) '(c d) );;;;复制所有列表内的参数,组成新列表.
(A B C D)
30 游程编码(run-length encoding)
* (defun n-elts (elt n);;;定义函数名及形参,此处elt为数据列表,非函数elt
(if (> n 1);;;;若n大于1,真,否则,假
(list n elt);;真,返回n 与elt组成新列表
elt));;假,返回elt数据列表
作用:判断n值是否大于1,若真,则返回n与原列表作为实参组成的新列表;若假则返回原列表。N-ELTS举例如下
* (n-elts '(4 5 6) 9)
(9 (4 5 6))
* (defun compr (elt n lst) ;;定义函数名及形参,此处elt 为数据列表,n为计数器,初始值不应大于1
(if (null lst) ;;判断列表是否为空,空则真,不空则假
(list (n-elts elt n));;;真,继续执行n-elts 程序,返回新列表(n elt),n≤1,则(elt)
(let ((next (car lst)));;假,定义变量next并赋lst列表的首值
(if (eql next elt);;;判断变量值与数据列表值是否为相同对象,相同,真;不同,假;(实质是判断lst列表中相邻两值是否相同)
(compr elt (+ n 1) (cdr lst));;真,循环执行compr,取n值+1,列表为首值后的新列表。
(cons (n-elts elt n) ;;假,执行n-elts 程序,返回新列表(n elt);n≤1,则(elt),确定出cons新组列表返回的第一个值
(compr next 1 (cdr lst))))))) ;;;lst首值作为next,从第一个开始判断,首值后元素组成新列表作为lst,循环执行开始程序
compr举例:
* (compr '( 3 5 7) 9 nil);;;;lst为空情况
((9 (3 5 7)));;返回elt与n组成的新列表(n不大于1则返回elt列表值)
* (compr 4 2 nil)
((2 4))
* (compr 4 1 '(3 4 4 4 3 4 5))
(4 3 (3 4) 3 4 5)
* (compr 4 0 '(3 4 4 4 3 4 5))
(4 3 (3 4) 3 4 5)
* (compr 4 2 '(3 4 4 4 3 4 5))
((2 4) 3 (3 4) 3 4 5)
* (compr 3 1 '(4 4 4 3 4 5))
* (defun compress (x) ;;定义函数名及形参
(if (consp x);;;判断实参是否为列表,是,真;否,假
(compr (car x) 1 (cdr x));;;;真,比较列表相邻两值是否相等,并统计出连续相同值得个数。取(car x)值到elt,n为1,(cdr x)到lst
x));;;假,直接返回x值
COMPRESS函数举例:
* (compress '(1 1 1 0 1 0 0 0 0 人 人 人))
((3 1) 0 1 (4 0) (3 人))
* (compress '(1 1 1 0 1 0 0 0 0 1))
((3 1) 0 1 (4 0) 1)
过程分析:
(compress '(1 1 1 0 1 0 0 0 0 1));;x为 '(1 1 1 0 1 0 0 0 0 1)
执行:(if (consp x);;; (1 1 1 0 1 0 0 0 0 1)非空列表,真
执行:(compr (car x) 1 (cdr x)) 即(compr 1 1 (1 1 0 1 0 0 0 0 1));;compr (elt n lst)注意位置对应的变量名。elt存储了x列表第一个值,n赋值为1,lst存储了新列表(cdr x)
执行:(if (null lst)即(if (null (1 1 0 1 0 0 0 0 1)) ;;不空,假
不执行:(list (n-elts elt n));;;真
执行:(let ((next (car lst)))即(let ((next (car (1 1 0 1 0 0 0 0 1))));;假,取(x列表第二个值)lst列表首值存储到变量next
执行:(if (eql next elt)即(if (eql 1 1);;;比较变量中对象是否相同,elt中存储x列表第一个值(car x),next存储了lst列表第一个值(car lst)(即x列表第二个值)。实质是判断lst列表中,从第一个位置开始,相邻两值是否相同),相同,真;不同,假
执行:(compr elt (+ n 1) (cdr lst))即(compr 1 2 (1 0 1 0 0 0 0 1));;真,elt变量中存储的值仍为(x列表第一个值)1,n存储器中值增加1为(+ n 1)=2,lst列表更新为(cdr lst)为(1 0 1 0 0 0 0 1)新lst列表。
2执行:(if (null lst)即(if (null (1 0 1 0 0 0 0 1)) ;;不空,假
2不执行:(list (n-elts elt n));;;真
2执行:(let ((next (car lst)))即(let ((next (car (1 0 1 0 0 0 0 1))));;假,取(x列表第三个值)新lst列表首值存储到变量next
2执行:(if (eql next elt)即(if (eql 1 1);;;比较变量中对象是否相同,elt中存储列表第一个值(car x),next存储了新lst列表第一个值(car lst)(即x列表第三个值)。实质是判断lst列表中第一个值和第三个值是否相同),相同,真;不同,假
2执行:(compr elt (+ n 1) (cdr lst))即(compr 1 3 (0 1 0 0 0 0 1));;真,elt变量中存储的值仍为(x列表第一个值)1,n存储器中值增加1为(+ n 1)=3,新lst列表更新为(cdr lst)为(0 1 0 0 0 0 1) 新lst列表2。
3执行:(if (null lst)即(if (null (0 1 0 0 0 0 1)) ;;不空,假
3不执行:(list (n-elts elt n));;;真
3执行:(let ((next (car lst)))即(let ((next (car (0 1 0 0 0 0 1))));;假,取(x列表第四个值)新lst列表2首值存储到变量next
3执行:(if (eql next elt)即(if (eql 0 1);;;比较变量中对象是否相同,elt中存储列表第一个值(car x),next存储了新lst列表2第一个值(car lst)(即x列表第四个值)0。实质是判断lst列表中第一个值和第四个值是否相同),相同,真;不同,假
3不执行:(compr elt (+ n 1) (cdr lst));;真
3执行:(cons (n-elts elt n) 即(cons (n-elts 1 3) ;;假,执行n-elts 程序,返回新列表(n elt);n≤1,则(elt)。返回列表(3 1)并暂存到cons函数容器中,等待最终合成
3执行:(compr next 1 (cdr lst))))))) ;;;继续执行(compr 0 1 (cdr lst)),next(即x列表第四个值)中的0值做为新的elt,n赋值1,新lst列表2更新为(cdr lst)为(1 0 0 0 0 1) 新lst列表3。
4执行:(if (null lst)即(if (null (1 0 0 0 0 1)) ;;判断新lst列表3,不空,假
4不执行:(list (n-elts elt n));;;真
4执行:(let ((next (car lst)))即(let ((next (car (1 0 0 0 0 1))));;假,取(x列表第五个值)新lst列表3首值存储到变量next
4执行:(if (eql next elt)即(if (eql 1 0);;;比较变量中对象是否相同,elt中存储了列表第四个值0,next存储了新lst列表3第一个值(car lst)(即x列表第五个值)1。实质是判断lst列表中第四个值和第五个值是否相同),相同,真;不同,假
4不执行:(compr elt (+ n 1) (cdr lst));;真
4执行:(cons (n-elts elt n) 即(cons (n-elts 0 1) ;;假,执行n-elts 程序,返回新列表(n elt);n≤1,则(elt)。返回列表(0)并暂存到cons函数容器中,等待最终合成
4执行:(compr next 1 (cdr lst))))))) ;;;继续执行(compr 1 1 (cdr lst)),next(即x列表第五个值)中的1值做为新的elt,n赋值1,新lst列表3更新为(cdr lst)为(0 0 0 0 1) 新lst列表4。
5执行:(if (null lst)即(if (null (0 0 0 0 1)) ;;新lst列表4不空,假
5不执行:(list (n-elts elt n));;;真
5执行:(let ((next (car lst)))即(let ((next (car (0 0 0 0 1))));;假,取(x列表第六个值)新lst列表4首值存储到变量next
5执行:(if (eql next elt)即(if (eql 0 1);;;比较变量中对象是否相同,elt中存储x列表第五个值1,next存储了新lst列表4第一个值(carlst)(即x列表第六个值)0。实质是判断lst列表中第五个值和第六个值是否相同),相同,真;不同,假
5不执行:(compr elt (+ n 1) (cdr lst));;真
5执行:(cons (n-elts elt n) 即(cons (n-elts 1 1) ;;假,执行n-elts 程序,返回新列表(n elt);n≤1,则(elt)。返回列表(1)并暂存到cons函数容器中,等待最终合成
5执行:(compr next 1 (cdr lst))))))) ;;;继续执行(compr 0 1 (cdr lst)),next(即x列表第六个值)中的0值做为新的elt,n赋值1,新lst列表4更新为(cdr lst)为( 0 0 0 1) 新lst列表5。
6执行:(if (null lst)即(if (null (0 0 0 1)) ;;新lst列表5不空,假
6不执行:(list (n-elts elt n));;;真
6执行:(let ((next (car lst)))即(let ((next (car (0 0 0 1))));;假,取(x列表第七个值)新lst列表5首值存储到变量next
6执行:(if (eql next elt)即(if (eql 0 0);;;比较变量中对象是否相同,elt中存储x列表第六个值0,next存储了新lst列表5第一个值(car lst)(即x列表第七个值)0。实质是判断lst列表中第六个值和第七个值是否相同),相同,真;不同,假
6执行:(compr elt (+ n 1) (cdr lst));;真.;继续执行(compr 0 2 (cdr lst)),elt变量中存储的值为0(x列表第六个值),n存储器中值增加1为(+ n 1)=2,新lst列表5更新为(cdr lst)为( 0 0 1) 新lst列表6。
7执行:(if (null lst)即(if (null (0 0 1)) ;;新lst列表6不空,假
7不执行:(list (n-elts elt n));;;真
7执行:(let ((next (car lst)))即(let ((next (car (0 0 1))));;假,取(x列表第八个值)新lst列表6首值0存储到变量next
7执行:(if (eql next elt)即(if (eql 0 0);;;比较变量中对象是否相同,elt中存储x列表第六个值0,next存储了新lst列表6第一个值(car lst)(即x列表第八个值)0。实质是判断lst列表中第六个值和第八个值是否相同),相同,真;不同,假
7执行:(compr elt (+ n 1) (cdr lst));;真.;继续执行(compr 0 3 (cdr lst)),elt变量中存储的值为0(x列表第六个值),n存储器中值增加1为(+ n 1)=3,新lst列表6更新为(cdr lst)为( 0 1) 新lst列表7。
8执行:(if (null lst)即(if (null (0 1)) ;;新lst列表7不空,假
8不执行:(list (n-elts elt n));;;真
8执行:(let ((next (car lst)))即(let ((next (car (0 1))));;假,取(x列表第九个值)新lst列表7首值0存储到变量next
8执行:(if (eql next elt)即(if (eql 0 0);;;比较变量中对象是否相同,elt中存储x列表第六个值0,next存储了新lst列表7第一个值(car lst)(即x列表第九个值)0。实质是判断lst列表中第六个值和第九个值是否相同),相同,真;不同,假
8执行:(compr elt (+ n 1) (cdr lst));;真.;继续执行(compr 0 4 (cdr lst)),elt变量中存储的值为0(x列表第六个值),n存储器中值增加1为(+ n 1)=4,新lst列表7更新为(cdr lst)为(1) 新lst列表8。
9执行:(if (null lst)即(if (null (1)) ;;新lst列表8不空,假
9不执行:(list (n-elts elt n));;;真
9执行:(let ((next (car lst)))即(let ((next (car (1))));;假,取(x列表第十个值)新lst列表8首值1存储到变量next
9执行:(if (eql next elt)即(if (eql 1 0);;;比较变量中对象是否相同,elt中存储x列表第六个值0,next存储了新lst列表8第一个值(car lst)(即x列表第十个值)1。实质是判断lst列表中第六个值和第十个值是否相同),相同,真;不同,假
9不执行:(compr elt (+ n 1) (cdr lst));;真
9执行:(cons (n-elts elt n) 即(cons (n-elts 0 4) ;;假,执行n-elts 程序,返回新列表(n elt);n≤1,则(elt)。返回列表(4 1)并暂存到cons函数容器中,等待最终合成
9执行:(compr next 1 (cdr lst))))))) ;;;继续执行(compr 1 1 (cdr lst)),next(即x列表第十个值)中的1值做为新的elt,n赋值1,新lst列表8更新为(cdr lst)为(nil) 新lst列表9。
10执行:(if (null lst)即(if (null (nil)) ;;新lst列表9空,真
10执行:(list (n-elts elt n))即(list (n-elts 1 1));;;真,执行n-elts 程序,返回新列表(n elt);n≤1,则(elt)。返回列表(1),并暂存到cons函数容器中,等待最终合成。
;;;返回(1)到9
10不执行:(let ((next (car lst)));;
10不执行:(if (eql next elt);;
10不执行:(compr elt (+ n 1) (cdr lst));;真
10不执行:(cons (n-elts elt n) (compr next 1 (cdr lst))))))) ;;;
9执行:(cons '(4 0) '(1)) ;;;返回8
( (4 0) 1)
6-8不执行:(cons (n-elts elt n) (compr next 1 (cdr lst))))))) ;;返回到上层
5执行:(cons '(1) (cons '(4 0) '(1)) ) ;;;返回开始4
((1) (4 0) 1)
4执行:(cons '(0) (cons '(1) (cons '(4 0) '(1) )) ) ;;;返回开始3
( (0) (1) (4 0) 1)
3执行:(cons '(3 1) (cons '(0) (cons '(1) (cons '(4 0) '(1) )) )) ;;;返回开始2
((3 1) (0) (1) (4 0) 1)
2不执行:(cons (n-elts elt n) (compr next 1 (cdr lst))))))) ;;返回开始
不执行:(cons (n-elts elt n) (compr next 1 (cdr lst))))))) ;;返回终值
总结:递归原理即确定解决问题的规则,每一层级按此规则解决问题(看做末端,得出返回值即认为此层程序执行完毕),并把每层计算的数值或nil或t返回,代入到上一层级,最终在顶层输出程序段的最终语句所指定的表达式值。(层层向下分拆逻辑关系,层层向上归纳返回值。
31/ elt函数,返回列表某个位置的值(elt lst n),列表起始位置为0,依次往后累加
(elt '(1 2 35 7 8 ) 4);;;返回列表第4位置的值
8;;结果