2.2
(/ (- 7 1) (- 4 2))
从左到右,对每一个参数求值:7求值为7,1求值为1.这两个传给-函数,返回6.(- 4 2)返回2.6跟2传给函数/,返回3.
2.5 函数
用defun来定义函数,一个函数通常有3个以上的参数,第一个是名字,第二个是参数列表,第三个是一个或者多个表达式。一个函数的返回值打算被解释成真或假,则此函数被称为判断式,通常以p结尾.如:(listp 27),判断27是否为列表,返回nil.nil既表示真也用来表示空.
2.6 if/and/or表达式
(if (listp ‘(a b c)) (+ 1 2) (+ 5 6)):判断是否为列表,返回:3
(and t (+ 1 2)):返回最后一个为真的参数:3.
(or t nil):返回最后一个为真的参数:t.
car函数是取出列表的第一个元素,cdr函数是取出列表第一个元素后面所有的元素.写一个函数,判断某个对象是否为列表的元素:
(defun our-member (obj 1st)
(if (null 1st)
nil
(if (eql (car 1st))
1st
(our-member obj (cdr 1st)))))
实用的方法是先在纸上写出伪代码,如下:
defun our-menber (obj 1st)
if null 1st
nill
if eql (car 1st) obj
1st
our-member obj (cdr 1st)
需要注意的一点是,如果编辑器不能做到匹配括号,想想如何让它做到。否则的话,这个编辑器就别用了。
2.9输入和输出
format函数打印:2 PLUS 3 EQUALS 5。format函数返回值:nil。~A表示会被后面的参数填入,~%表示换行。
(format t "~A plus ~A equals ~A. ~%" 2 3 (+ 2 3))
2 PLUS 3 EQUALS 5
nil
下面的代码是标准输入
(defun askem (string)
(format t "~A" string)
(read))
> (askem "How old are you?")
How old are you? 29
29
2.10变量
let表达式有两个部分,第一个部分是一系列创造新变量的指令。第二部分是表达式的主题,即调用函数+
> (let ((x 1) (y 2))
(+ x y))
3
下面的代码是判断输入的对象是否为数字,如果是则输出该数字,如果不是,则重新输入。
(defun ask-number ()
(format t "Please enter a number. ")
(let ((val (read)))
(if (numberp val)
val
(ask-number))))
定义全域变量/全域常量/判断一个对象是否为全域变量或者全域常量
> (defparameter *glob* 99)
*GLOB*
(defconstant limit (+ *glob* 1))
> (boundp '*glob*)
T
2.11赋值
如果 setf 的第一个参数是一个符号(symbol),且这个符号不是某个局部变量的名字,那么setf 将设置这个符号为全局变量:
> (setf x (list 'a 'b 'c))
(A B C)
也就是说,通过赋值,你可以隐式地创建全局变量。 不过,一般来说,还是使用defparameter 显式地创建全局变量比较好。
你不仅可以给变量赋值。传入 setf 的第一个参数,还可以是一个表达式或一个变量名。在这种情况下,第二个参数的值被插入至第一个参数所引用 (refer)的地方:
> (setf (car x) 'n)
N >
x
(N B C)
2.13迭代
打印start到end之间整数的平方。
(defun show-squares (start end)
(do ((i start (+ i 1)))
((> i end) 'done)
(format t "~A ~A~%" i (* i i))))
下面这个是递归的写法
(defun show-squares (i end)
(if (> i end)
'done
(progn
(format t "~A ~A~%" i (* i i))
(show-squares (+ i 1) end))))
还写了一个dolist的迭代函数,没怎么看懂。
## 3.1构建 ##
把两个对象结合成一个有两部分的对象,称之为 Cons 对象,Cons 的一半来指向列表的第一个元素,然后用另一半指向列表其余的元素(可能是别的 Cons 或 nil )。 Lisp 的惯例是使用 car 代表列表的第一个元素,而用 cdr 代表列表的其余的元素。下面的简单代码表示,Cons对象第一个元素是a,第二个元素是nil,第二个元素也可以是列表。
> (setf x (cons 'a nil))
(A)
3.2等式
当我们给 y 赋一个相同的值时, Lisp 复制的是指针,而不是列表。所以无论何时你把一个变量赋给另一个变量时,两个变量会有相同的值(eql)。
> (setf x '(a b c))
(A B C)
> (setf y x)
(A B C)
> (eql x y)
T
如果要复制另外一份列表,需要这么做
> (setf x '(a b c)
y (copy-list x))
(A B C)
3.6存取
找到列表特定位置的元素,我们可以调用nth;而要找到第n个cdr,我们调用nthcdr,需要注意的是,nthcdr返回的是Cons对象,代码如下:
> (nth 0 '(a b c))
A
> (nthcdr 2 '(a b c))
(C)
函数 last 返回列表的最后一个 Cons 对象
> (last '(a b c))
(C)
3.7映射函数,对列表中的每个元素作函数调用
以mapcar跟maplist为例,mapcar是把函数应用至每个列表的元素的结果,直到有的列表没有元素为止maplist是讲列表下一个渐进的cdr传入函数。#’是把函数转换成对象的写法,lambda是定义函数的写法,mapc跟mapcan会在后面讨论。
> (mapcar #'(lambda (x) (+ x 10))
'(1 2 3))
(11 12 13)
> (mapcar #'list
'(a b c)
'(1 2 3 4))
((A 1) (B 2) (C 3))
> (maplist #'(lambda (x) x)
'(a b c))
((A B C) (B C) (C))
对第一个例子稍微的修改,可以实现对两个列表相同索引位置元素的加法。也就是说列表的个数跟你自己定义的函数有关。
> (mapcar #'(lambda (x y) (+ x y))
'(1 2 3)
'(2 3 4))
(3 5 7)