Common Lisp通过DEFUN宏来定义新的函数
(defun name (parameter*)
"optional documentation string."
body-form*)
这里函数的名字几乎可以是任意的符号/字符组合,一般是小写字母和连字符。如:
(defun hello-world () (format t "hello, world~%"))
可选参数(optional parameters)
(defun foo (p1 p2 &optional p3 p4) (list p1 p2 p3 p4))
&optional后的参数p3 p4都是可选的,运行该函数后的结果可能如下:
(foo 1 2) -> (1 2 NIL NIL)
(foo 1 2 3) -> (1 2 3 NIL)
(foo 1 2 3 4) -> (1 2 3 4)
还可以通过如下方式指定可选参数的默认值
(defun foo (p1 &optional (b 10)) (list a b))
运行该函数后的结果可能如下:
(foo 1) -> (1 10)
(foo 1 2) -> (1 2)
如果需要判断可选参数是否由客户端(caller)指定了值,可以通过在默认值表达式后添加一个特殊的变量来获取。这个变量的名字为可选参数名加上后缀"-supplied-p",当客户端指定了具体值,该变量为true,否则为NIL。如:
(defun foo (p1 &optional (p2 2 p2-supplied-p)) (list p1 p2 p2-supplied-p))
可能的运行结果为:
(foo 1) -> (1 2 NIL)
(foo 1 0) -> (1 0 T)
(foo 1 2) -> (1 0 T)
变长参数(Rest Parameters)
(defun foo (p1 &rest others) (list p1 others))
函数FOO必须的参数个数为1,其他剩余的参数将组成一个list存到&rest后的变量others中。运行该函数的可能结果:
(foo 1 'a 'b 'c) -> (1 (A B C))
可变长参数的最大支持的个数以来不同的lisp实现,但是最少都支持50个。
关键字参数(Keyword Parameters)
(defun foo (&key p1 p2 p3) (list p1 p2 p3))
&key后的参数为keyword parameters,可以有任意个数。当FOO被调用时,每个keyword parameter都被绑定为了指定的值(或默认值),同时由于keyword parameters被标签化了,所以可以任意顺序的指定它们的值,如:
(foo) -> (NIL NIL NIL)
(foo :p1 1 :p2 'a) -> (1 'a NIL)
(foo :p1 1 :p3 3) -> (1 NIL 3)
(foo :p2 2 :p1 'first :p3 3) -> (FIRST 2 3)
也可以用类似设定optional parameter默认值的方式设定keword parameters的默认值:
(defun foo (&key (p1 1) (p2 2 p2-supplied-p) (p3 (+ p1 p2)))
(list p1 p2 p3 p2-supplied-p))
可能的运行结果如下:
(foo) -> (1 2 3 NIL)
(foo :p2 2) -> (1 2 3 T)
(foo :p1 10 :p2 20 :p3 30) -> (10 20 30 T)
混合不同类型的参数
可以在函数的参数列表中混合以上类型的参数,但是不太常用。但是他们须遵循如下顺序:1、必要的参数;2、可选参数(optional parameter);3、可变参数(rest parameter);4、关键字参数(keyword parameter)。如需用时再补充。
函数返回值
所有函数的默认返回值其最后一个表达式的值。也可以通过RETURN-FROM(后续补充)立即返回特定的值。
将函数作为数据(Functions As Data)
可以类比为C语言中的函数地址,要获取function object,可通过FUNCTION操作符或#':
(function foo) ;等价于 #'foo
当获取到function object后,可以通过FUNCALL或APPLY调用该函数。
FUNCALL,当在编码时已知将要传递给函数的参数个数时使用;
APPLY,则是将函数所需要的参数打包成一个list出入。
(defun foo (p1 p2) (format t "~d(P1) ~d(P2)~%))
(funcall #'foo 1 2) ; 输出为:1(P1) 2(P2)
(apply #'foo '(10 20)) ; 输出为:10(P1) 20(P2)
匿名函数(Anonymous Functions)
(lambda parameters) body)
比如:
(funcall #'(lambda (x y) (+ x y)) 2 3) -> 5