《ANSI COMMON LISP》讀書筆記
2. 給出3中不同表示(a b c)
的cons表達式。
(cons 'a '(b c))
(cons 'a (cons 'b (cons 'c '())))
(list 'a 'b 'c)
3. 使用car
與cdr
來定義一個函數,返回一個列表的第四個元素。
(defun fourth. (x)
(car (cdr (cdr (cdr x)))))
4. 定義一個函數,接受兩個實參,返回兩者當中比較大的那個。
(defun max. (x y)
(if (or (null x) (null y))
nil
(if (> x y) x y)))
5. 這些函數做兩個什么?
(a)
(defun enigma (x)
(and (not (null x))
(or (null (car x))
(enigma (cdr x)))))
(b)
(defun mystery (x y)
(if (null y)
nil
(if (eql (car y) x)
0
(let ((z (mystery x (cdr y))))
(and z (+ z 1))))))
(a):判斷列表x不為空表,但是存在空表元素,如此則返回t
,否則返回nil
。
(b):接受原子x
和列表y
作為參數,返回x
出現在列表y
中的位置。
(b)中最后一句代碼,當
y
中不存在x
元素時,z
為nil
,利用
and`的短路行為,不執行(+ z 1)。
6. 下列表達式,x
該是什么才會得到相同的結果?
(a) > (car (x (cdr '(a (b c) d))))
B
(b) > (x 13 (/ 1 0))
13
(c) > (x #'list 1 nil)
(1)
(a): car
(b): or
(c): apply
apply與funcall的區別
apply接受一個函數和一個實參列表,并返回把傳入參數應用于實參列表的結果。
apply 可以接受任意數量的實參,只要最后一個實參是列表即可。
函數funcall 做的是一樣的事情,但不需要把實參包裝成列表。
但apply和funcall是有區別的。
CL-USER> (apply #'list 1 nil)
(1)
CL-USER> (funcall #'list 1 nil)
(1 NIL)
CL-USER>
**區別**就在于參數使用方法是不同的。
- 對于
funcall
,在函數描述符之后的參數都是平等的,它們組合成一個列表讓函數調用。 - 對于
apply
,最后一個參數必須為列表,并且是將列表中的每個元素與其他參數作為平等的參數對待,然后收集組合成一個列表讓函數調用。
因此,
apply
認為nil
是一個空列表,內部無元素,與第一個參數組合是列表(1)
給list
調用。funcall
認為1
和nil
是獨立的兩個參數,組合后交給list
函數生成列表(1 nil)
。
7. 只是用本章所介紹的操作符,定義一個函數,它接受一個列表作為實參,如果有一個元素是列表時,就返回真。
(defun haslist (x)
(if (null x)
nil
(if (not (listp x))
nil
(if (listp (car x))
t
(haslist (cdr x))))))
8. 給出函數的迭代與遞歸版本:
a. 接受一個正整數,并打印出數字數量的點。
b. 接受一個列表,并返回a在列表里所出現的次數。
a迭代:
(defun print-asterisk (x)
(if (or (null x) (listp x))
nil
(do ((i 1 (+ i 1)))
((> i x) 'done)
(format t "*"))))
a遞歸:
(defun print-asterisk (x)
(if (or (null x) (listp x))
nil
(if (> x 0)
(progn
(format t "*")
(print-asterisk (- x 1)))
'done)))
b迭代:(這樣不完整,沒有辦法遍歷每個元素的元素)
(defun num-of-a (lst)
(if (or (null lst) (not (listp lst)))
nil
(let ((num 0))
(dolist (obj lst)
(if (eql 'a obj)
(setf num (+ num 1))))
num)))
b遞歸:(這才是真正的遞歸遍歷所有的分支)
(defun num-of-a (lst)
(if (or (null lst) (not (listp lst)))
0
(if (eql 'a (car lst))
(+ 1 (num-of-a (cdr lst)))
(if (listp (car lst))
(num-of-a (car lst))
(num-of-a (cdr lst))))))
CL-USER> (num-of-a '((a b (a c (a d))) e f))
3
CL-USER>
9. 一位朋友想寫一個函數,返回列表里所有非nil
元素的和。他寫了此函數的兩個版本,但兩個都不能工作。請解釋每一個的錯誤在哪里,并給出正確的版本。
(a)
(defun summit (lst)
(remove nil lst)
(apply #'+ lst))
(b)
(defun summit (lst)
(let ((x (car lst)))
(if (null x)
(summit (cdr lst))
(+ x (summit (cdr lst))))))
(a): remove并不能真正將lst里面的nil
去掉,而是其返回值中去掉了nil
。
(defun summit (lst)
(let ((x nil))
(setf x (remove nil lst))
(apply #'+ x)))
(b): 沒有停止條件
(defun summit (lst)
(if (null lst)
0
(let ((x (car lst)))
(if (null x)
(summit (cdr lst))
(+ x (summit (cdr lst)))))))