[关闭]
@yiltoncent 2015-03-19T17:42:35.000000Z 字数 2255 阅读 3112

《ANSI COMMON LISP》第二章 习题

LISP

《ANSI COMMON LISP》读书笔记


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

  1. (cons 'a '(b c))
  2. (cons 'a (cons 'b (cons 'c '())))
  3. (list 'a 'b 'c)

3. 使用carcdr来定义一个函数,返回一个列表的第四个元素。

  1. (defun fourth. (x)
  2. (car (cdr (cdr (cdr x)))))

4. 定义一个函数,接受两个实参,返回两者当中比较大的那个。

  1. (defun max. (x y)
  2. (if (or (null x) (null y))
  3. nil
  4. (if (> x y) x y)))

5. 这些函数做两个什么?

  1. (a)
  2. (defun enigma (x)
  3. (and (not (null x))
  4. (or (null (car x))
  5. (enigma (cdr x)))))
  6. (b)
  7. (defun mystery (x y)
  8. (if (null y)
  9. nil
  10. (if (eql (car y) x)
  11. 0
  12. (let ((z (mystery x (cdr y))))
  13. (and z (+ z 1))))))

(a):判断列表x不为空表,但是存在空表元素,如此则返回t,否则返回nil
(b):接受原子x和列表y作为参数,返回x出现在列表y中的位置。

(b)中最后一句代码,当y中不存在x元素时,znil,利用and`的短路行为,不执行(+ z 1)。

6. 下列表达式,x该是什么才会得到相同的结果?

  1. (a) > (car (x (cdr '(a (b c) d))))
  2. B
  3. (b) > (x 13 (/ 1 0))
  4. 13
  5. (c) > (x #'list 1 nil)
  6. (1)

(a): car
(b): or
(c): apply

apply与funcall的区别
apply接受一个函数和一个实参列表,并返回把传入参数应用于实参列表的结果。
apply 可以接受任意数量的实参,只要最后一个实参是列表即可。
函数funcall 做的是一样的事情,但不需要把实参包装成列表。
但apply和funcall是有区别的。

  1. CL-USER> (apply #'list 1 nil)
  2. (1)
  3. CL-USER> (funcall #'list 1 nil)
  4. (1 NIL)
  5. CL-USER>

区别就在于参数使用方法是不同的。

  • 对于funcall,在函数描述符之后的参数都是平等的,它们组合成一个列表让函数调用。
  • 对于apply,最后一个参数必须为列表,并且是将列表中的每个元素与其他参数作为平等的参数对待,然后收集组合成一个列表让函数调用。

因此apply认为nil是一个空列表,内部无元素,与第一个参数组合是列表(1)list调用。funcall认为1nil是独立的两个参数,组合后交给list函数生成列表(1 nil)

7. 只是用本章所介绍的操作符,定义一个函数,它接受一个列表作为实参,如果有一个元素是列表时,就返回真。

  1. (defun haslist (x)
  2. (if (null x)
  3. nil
  4. (if (not (listp x))
  5. nil
  6. (if (listp (car x))
  7. t
  8. (haslist (cdr x))))))

8. 给出函数的迭代与递归版本:

a. 接受一个正整数,并打印出数字数量的点。
b. 接受一个列表,并返回a在列表里所出现的次数。

a迭代:

  1. (defun print-asterisk (x)
  2. (if (or (null x) (listp x))
  3. nil
  4. (do ((i 1 (+ i 1)))
  5. ((> i x) 'done)
  6. (format t "*"))))

a递归:

  1. (defun print-asterisk (x)
  2. (if (or (null x) (listp x))
  3. nil
  4. (if (> x 0)
  5. (progn
  6. (format t "*")
  7. (print-asterisk (- x 1)))
  8. 'done)))

b迭代:(这样不完整,没有办法遍历每个元素的元素)

  1. (defun num-of-a (lst)
  2. (if (or (null lst) (not (listp lst)))
  3. nil
  4. (let ((num 0))
  5. (dolist (obj lst)
  6. (if (eql 'a obj)
  7. (setf num (+ num 1))))
  8. num)))

b递归:(这才是真正的递归遍历所有的分支)

  1. (defun num-of-a (lst)
  2. (if (or (null lst) (not (listp lst)))
  3. 0
  4. (if (eql 'a (car lst))
  5. (+ 1 (num-of-a (cdr lst)))
  6. (if (listp (car lst))
  7. (num-of-a (car lst))
  8. (num-of-a (cdr lst))))))
  9. CL-USER> (num-of-a '((a b (a c (a d))) e f))
  10. 3
  11. CL-USER>

9. 一位朋友想写一个函数,返回列表里所有非nil元素的和。他写了此函数的两个版本,但两个都不能工作。请解释每一个的错误在哪里,并给出正确的版本。

  1. (a)
  2. (defun summit (lst)
  3. (remove nil lst)
  4. (apply #'+ lst))
  5. (b)
  6. (defun summit (lst)
  7. (let ((x (car lst)))
  8. (if (null x)
  9. (summit (cdr lst))
  10. (+ x (summit (cdr lst))))))

(a): remove并不能真正将lst里面的nil去掉,而是其返回值中去掉了nil

  1. (defun summit (lst)
  2. (let ((x nil))
  3. (setf x (remove nil lst))
  4. (apply #'+ x)))

(b): 没有停止条件

  1. (defun summit (lst)
  2. (if (null lst)
  3. 0
  4. (let ((x (car lst)))
  5. (if (null x)
  6. (summit (cdr lst))
  7. (+ x (summit (cdr lst)))))))
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注