一:Block
Common Lisp has three basic operators for creating blocks Of code: progn, block, and tagbody.
block
A block is like a progn with a name and an emergency exit At any point within the body, you can halt evaluation and return a value immediately by using return-from with the block's name,
1:The second argument to return-from is returned as the value of the block named by the first。 return-from后第二个参数会作为以第一个参数为名字的block的值。
CL-USER> (block head (format t "Here we go.") (return-from head 'idea) (format t "We'll never see this.")) Here we go. IDEA
2:There is also a return macro, which returns its argument as the value of an enclosing block named nil
CL-USER> (block nil (return 27)) 273: Common lisp 有一些操作符比如接受很多body 的话,他会把这些body 隐式的包含在一个叫nil 的block 里面,
CL-USER> (dolist (x '(a b c d e)) (format t "~A " x) (if (eql x 'c) (return 'done))) A B C DONE
4:用defun定义的function,也会隐式的把body放入到跟他的function name一样的block里面。
CL-USER> (defun foo () (return-from foo 27)) FOO
Outside of an explicit or implicit block, neither return-from nor return will work.
Tagbody
The third basic block construct is tagbody, within which you can use gotos. Atoms appearing in the body are interpreted as labels;Most iteration operators have an implicit tagbody, but Most programmers will never use tagbody explicitly.
CL-USER> (tagbody (setf x 0) top (setf x (+ x 1)) (format t "~A " x) (if (< x 10) (go top))) 1 2 3 4 5 6 7 8 9 10 NIL二:Condition
If /when/unless
下面这个代码基本上也就概述了if/progn的实现功能。因为then-form和else-form都必须为单一的lisp形式,progn可以顺序执行任何数量的形式并返回最后一个形式的值。
CL-USER> (when (oddp that) (format t "Hmm, that's odd.") (+ that 1)) Is equivalent to: CL-USER> (if (oddp that) (progn (format t "Hmm, that's odd.") (+ that 1))) CL-USER> (unless nil (format t "~a " 'first) (format t "~a " 'second)) FIRST SECOND NILCond
当遇到多分支的条件语句时
Cond allow multiple condition. (test-1 form),只要test为真就执行form,下面的不再执行。t为不得已的处理方式
CL-USER> (defun our-member (obj 1st) (cond ((atom 1st) nil) ((eql (car 1st) obj) 1st) (t (our-member obj (cdr 1st)))))
Case: begins with an argument whose value will be compared against the keys in each clause
CL-USER> (defun month-length (mon) (case mon ((jan mar may jul aug oct dec) 31) ((apr jun sept nov) 30) (feb (if (leap-year) 29 28)) (otherwise "unknown month")))AND ,OR 和 NOT
Not 接受单一形参,并对真值求反
AND 和OR都支持“短路”特性
AND 只有有一个表达式为NIL,就返回NIL,到最后无NIL,返回最后一个表达式的值。
OR 只要一个子表达式值为非NIL,就返回当前值,否则就返回NIL
三:Iterator Operator
The basic iteration operator is do, Since do contains both an implicit block and an implicit tagbody, we now know that it's possible to use return, return-f rom, and go within the body of a do
DOLIST和DOTIMES
列表循环和计数循环,这两个宏提供易用却不怎么通用的结构,对于其他无法满足的情形,仍需要在DO之上构建自定义的循环结构
The third expression within the initial list will be evaluate and return as the value of dolist /dotimes when iteration terminates, it defaults to nil.
(dolist (var list-form) body-form*) var依次从list中取出后继来执行循环体
(dotimes (var count-form) body-form*),var持有的整数从0逐增到比那个数小1
CL-USER> (dolist (x '(1 2 3))(print x)) 1 2 3 NIL CL-USER> (dolist (x '(1 2 3))(print x)(if (evenp x)(return x))) 1 2 2 CL-USER> (dotimes (i 4)(print i)) 0 1 2 3 NIL
DO
(do (variable-definition*)
(end-test-form result-form*)
Statement*)
Variable-definition是含有三个元素的列表(var init-form step-form)
init-form 开始绑定到var, 在下一次循环开始之前step-form赋值给var。 如果step-form未给的话,var将保持不变。如果init-form未给,将绑定到NIL。
当循环开始以后,循环变量都被赋予了新值后,即新一轮的循环开始end-test-form(终止判断条件)会被求值。
当end-test-form为真时,result-form(结果形式)将被求值,
在迭代的每一步,所有的step-form将在分配任何值给变量之前被求值。意味着可以在step-form中引用其他任何循环变量(比如下面就是这一轮的用上一轮的结果)理解do循环的关键部分。为啥它能够循环执行,因为它里面还有一个判断条件(end-test-form),有一点类似java中的do while.而现在由于本身变量定义就有一定的逻辑(第三个参数给变量赋值),所以有的时候就不需要循环体之类的,并且有的时候会省略结果形式,
(= 10 n)是一个终止条件,cur 当end-test-form为真是才求值。并且因为先给参数赋予新值才会判断终止条件,所以当判断到n=10时,上一轮的next已经变为这一轮的cur.所以最后的结果cur返回了第11个斐波那契数。
CL-USER> (do ((n 0 (+ 1 n)) (cur 0 next) (next 1 (+ cur next))) ((= 10 n) cur)) 55 上面就是斐波那契 省略了结果形式,(print i)只是一个打酱油的,即statement,最后整个式子的值是NIL. CL-USER> (do ((i 0 (1+ i))) ((>= i 4)) (print i)) 0 1 2 3 NIL
以上有的时候容易造成一些混乱,关键记住6个括号是do循环必备的,
一对括号围住变量声明
一对围住终止测试和结果形式
一对围住整个表达式
(do (variable-definition*)
(end-test-form result-form*)
Statement*)
下面的例子是即使没有没有循环变量,仍需给个变量的空列表。实现的功能是,在当前时间小于一个全局变量的时候,保持循环,每分钟打印一个waiting.
CL-USER> (get-universal-time) 3551729050 CL-USER> (do () ((> (get-universal-time) *some-future-date*)) (format t "waiting ~%") (sleep 60))四:Context
Let allow us to establish new variables for use within the body, because entering a let is conceptually equivalent to doing a function call
CL-USER> (let ((x 7) (y 2)) (format t "Number") (+ x y)) Number 9 The preceding l e t expression is exactly equivalent to: CL-USER> ((lambda (x y) (format t "Number") (+ x y)) 7 2)
由于我们给出了let的等价形式,那么下面这个问题就很显而易见了。
CL-USER> (let ((x 2) (y (+ x 1))) (+ x y)); undefined variable: X 因为上式等价于 CL-USER> ((lambda (x y) (+ x y)) 2 (+ x 1))
Here it's obvious that the (+ x 1) passed as an argument to the function cannot refer to the parameter x within the function.
如果你想用这个X的话,就调用Let*方法。
CL-USER> (let* ((x 1) (y (+ x 1))) (+ x y)) 3 A let* is functionally equivalent to a series of nested lets . CL-USER> (let ((x 1)) (let ((y (+ x 1))) (+ x y)))五:Multiple Values
The maximum number of return values is implementation-dependent, but it will be at least 19.
Multiple values allow a function that calculates several things to return them without having to build a structure to contain them all. For example, the built-in get-decoded time returns the current time in nine values: second,minute, hour, date, month, day, and two others.
Multiple values also make it possible to have lookup functions that can distinguish between finding nil and failing to find something.
The values function returns multiple values CL-USER> (multiple-value-bind (x y z) (values 1 2 3) (list x y z)) (1 2 3) More variables than values CL-USER> (multiple-value-bind (x y z) (values 1 2) (list x y z)) (1 2 NIL) Move values than variables CL-USER> (multiple-value-bind (s m h) (get-decoded-time) (format nil "~A:~A:~A" h m s)) "19:17:13"
multiple-value-call pass on multiple values as the arguments to a second function
multiple-value-list like multiple-value-call with #'list as the first argument
CL-USER> (multiple-value-call #' + (values 1 2 3)) 6 CL-USER> (multiple-value-list (values 'a 'b ' c)) (A B C)