《ANSI COMMON LISP》第二章 習題


《ANSI COMMON LISP》讀書筆記


2. 給出3中不同表示(a b c)的cons表達式。

(cons 'a '(b c))
(cons 'a (cons 'b (cons 'c '())))
(list 'a 'b 'c)

3. 使用carcdr來定義一個函數,返回一個列表的第四個元素。

(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元素時,znil,利用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認為1nil是獨立的兩個參數,組合后交給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)))))))
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容