ANSI Common Lisp 学习笔记 第二章
可以用org-mode来查看
* 第二章:欢迎来到 Lisp
** #'third
** #'null
** #'listp
** #'if
(if () ( ) ( ))
** #'and (macro)
** #'or (macro)
** #'format
(format t "~A plus ~A equals ~A. ~%" 2 3 (+ 2 3))
注意到有两个东西被打印出来。第一行是 format 印出来的。第二行是调用 format 函数的返回值,就像平常顶层会打印出来的一样。通常像 format 这种函数不会直接在顶层调用,而是在程序内部里使用,所以返回值不会被看到。
format 的第一个实参 t ,表示输出被送到缺省的地方去。通常是顶层。第二个实参是一个用作输出模版的字串。在这字串里,每一个 ~A 表示了被填入的位置,而 ~% 表示一个换行。这些被填入的位置依序由后面的实参填入。
** #'read 当没有实参时,会读取缺省的位置,通常是顶层。下面这个函数,提示使用者输入,并返回任何输入的东西
** #'let
(let [() ( ) ( )] (body))
** #'let*
** #'numberp
** #'defparameter 全局变量在任何地方都可以存取,除了在定义了相同名字的区域变量的表达式里。为了避免这种情形发生,通常我们在给全局变量命名时,以星号作开始与结束。刚才我们创造的变量可以念作 “星-glob-星” (star-glob-star)。
** #'defconstant 我们不需要给常量一个独一无二的名字,因为如果有相同名字存在,就会有错误产生 (error)。
** #'boundp 如果你想要检查某些符号,是否为一个全局变量或常量,使用 boundp 函数
** #'setf
(setf. . . . . . )
可以用来给全局或局部变量赋值 如果 setf 的第一个实参是一个符号(symbol),且这个符号不是某个局部变量的名字,那么 setf 将设置这个符号为全局变量: > (setf x (list 'a 'b 'c))
(A B C)
也就是说,通过赋值,你可以隐式地创建全局变量。 不过,一般来说,还是使用 defparameter 明确地创建全局变量比较好。 > (setf (car x) 'n) N > x
(N B C)
** #'remove 函数 remove 接受一个对象和一个列表,返回不含这个对象的新列表 原来的表没有被改变
(remove 'a '(a d c a d))
** #'do (macro)
(defun show-squares (start end) (do ((i start (+ i 1))) ((> i end) 'done) (format t "~A, ~A~%" i (* i i))))
(show-squares 4 6)
(do (()) (( ... )) )
** #'progn
** #'dolist
(dolist (object /as in/ list) (do sth about object))
(defun print-list (lst) (dolist (obj lst) (if (listp obj) (print-list obj) (format t "~A~%" obj))))
(print-list '(1 (7 2) 4 6))
** #'function apply funcall
(function *)
(apply #'* '(2 2))
(funcall #'+ 1 2 6)
(funcall #'(lambda (x) (* x x)) 2)
** typep
(typep 27 'integer)
** Exercise
*** 1 描述下列表达式求值之后的结果
(a)
(+ (- 5 1) (+ 3 7))
(b)
(list 1 (+ 2 3))
(c)
(if (listp 1) (+ 1 2) (+ 3 4))
(d)
(list (and (listp 3) t) (+ 1 2))
*** 2 给出 3 种不同表示 (abc) 的 cons 表达式
(cons 'a (cons 'b (cons 'c nil)))
(cons 'a (cons 'b '(c)))
(cons 'a '(b c))
*** 3 使用 car 与 cdr 来定义一个函数,返回一个列表的第四个元素。
(defun my-fourth (lst) (labels ((n-th (lst n);assert n > 0 (if (and (> n 0) (not (null (cdr lst))) (listp (cdr lst))) (n-th (cdr lst) (- n 1)) (if (= n 0) (car lst) nil)))) (n-th lst 3)))
(my-fourth (list '(1 2 3) '(1 3 5) '(4 5 6) '(4)))
*** 4 定义一个函数,接受两个实参,返回两者当中较大的那个。
(defun greater (a b &optional (test-func #'>)) (if (funcall test-func a b) a b))
(greater 'a 'b #'(lambda (a b) nil))
*** 5 这些函数做了什么? (a)
(defun enigma (x) (and (not (null x)) (or (null (car x));(nil . else . else) (enigma (cdr x));(sth . nil . esle)) )))
;; guess: a test of structure (* . nil . else)
(enigma '(1 nil 3)) (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))))))
;; find if y contains x and return position
(mystery 5 '(1 2 3 4 5 6 7))
*** 6 下列表达式, x 该是什么,才会得到相同的结果? (a) >
(car (x (cdr '(a (b c) d))))
B ?
(car (car (cdr '(a (b c) d))))
(b) >< pre class="brush: lisp">(x 13 (/ 1 0)) 13 ?
(or 13 (/ 1 0))
(c) >
(x #'list 1 nil)
(1) ?
(apply #'list 1 nil)
*** 7 只使用本章所介绍的操作符,定义一个函数,它接受一个列表作为实参,如果有一个元素是列表时,就返回真。
(defun contains-list-p (lst) (let ((result nil)) (dolist (obj lst) (if (listp obj) (setf result t))) result))
(contains-list-p '(1 (2 5 7) 3))
*** 8 给出函数的迭代与递归版本: a 接受一个正整数,并打印出数字数量的点。 b 接受一个列表,并返回 a 在列表里所出现的次数。
(defun eight-a-init (int) (if (< int 0) nil (do ((i int (- i 1))) ((= i 0) nil) (format t "."))))
(defun eight-a-recursive (int) (if (<= int 0) nil (progn (format t ".") (eight-a-recursive (- int 1)))))
;(eight-a-init 1) ;(eight-a-recursive -1)
*** 9 一位朋友想写一个函数,返回列表里所有非 nil 元素的和。他写了此函数的两个版本,但两个都不能工作。请解释每一个的错误在哪里,并给出正确的版本。 (a)
(defun summit (lst) (remove nil lst) (apply #'+ lst))
;; remove 无副作用,应为
(defun summit (lst) (apply #'+ (remove nil lst)))
(b)
(defun summit (lst) (let ((x (car lst))) (if (null x) (summit (cdr lst)) (+ x (summit (cdr lst))))))
;;明显是出口没留出来,应为 (b)
(defun summit (lst) (if (null lst) 0 (let ((x (car lst))) (if (null x) (summit (cdr lst)) (+ x (summit (cdr lst)))))))