LISP笔记(函数)(LISP里NULL在书中一般表示NIL, 这里用了小写nil)
函数是一种对代码比特进行抽象的标准方式
函数的一般定义:
(defun name (parameter*)
“Optional documentation string.”
Body-form*)
函数名用-分隔符更具有lisp风格。
Lisp(helloworld程序): (defun hello-world() (format t “hello, world”)).
一:可选形参,关键字(optional)
1)可选形参,关键字(optional)
(defun foo (a b &optional c d) (list a b c d))
(foo 1 2) -> (1 2 nil nil)
(foo 1 2 3) -> (1 2 3 nil)
(foo 1 2 3 4) -> (1 2 3 4)
2)也可以为可选形参提供指定默认值而不是NIL
(defun foo (a &optional (b 10)) (list a b))
(foo 1 2) -> (1 2)
(foo 1) -> (1 10)
3)也可以:
(defun make-rectangle (width &optional (height width)) ...)
除非明确指定否则height形参带有和width形参相同的值
4)判断可选形参是否用了默认值: -supplied-p.
(defun foo (a b &optional (c 3 c-supplied-p))
(list a b c c-supplied-p))
(foo 1 2) -> (1 2 3 nil)
(foo 1 2 3) -> (1 2 3 T)
(foo 1 2 4) -> (1 2 4 T)
二:剩余形参 (可变数量的实参)
1)剩余形参,关键字:&rest
满足了必要和可选形参之后的其余所有实参被收集到一个列表里成为该&rest形参的值
Format函数为:(defun format (stream string &rest values) ...)
+函数则为: (defun + (&rest numbers) ...)
三:关键字形参, 关键字:&key
1)关键字形参(&key)
(defun foo (&key a b c)
(list a b c))
(foo) -> (nil nil nil)
(foo :a 1) -> (1 nil nil)
(foo :a 1 :c 3) -> (1 nil 3)
(foo :a 1 :c 3 :b 2) -> (1 2 3)
2)提供默认值形式及一个supplied-p变量名
(defun foo (&key (a 0) (b 0 b-supplied-p) (c (+ a b)))
(list a b c b-supplied-p))
(foo :a 1) -> (1 0 1 nil)
(foo :b 1) -> (0 1 1 T)
(foo :a 2 :b 1 :c 4) -> (2 1 4 T)
3)将形参名替换成一个列表,调用函数时使用关键字以及用作形参的名字
(defun foo (&key ((:apple a)) ((:box b) 0) ((:charlie c) 0 c-supplied-p))
(list a b c c-supplied-p))
(foo :apple 10 :box 20 :charlie 30) -> (10 20 30 T) //用在API与其内部隔离
四:函数返回值
RETURN-FROM
(defun foo (n)
(dotimes (i 10)
(dotimes (j 10)
(when (> (* i j) n)
(return-from foo (list i j))))))
五:高阶函数 (函数是一种对代码比特进行抽象的标准方式)
Defun 定义一个函数作了两件事:创建一个新的函数对象以及赋予其一个名字。
1)获取一个函数的方法 FUNCTION (#’是FUNCTIONDE 语法糖)
(defun foo (x) (* 2 x))
(function foo) == #’foo
2)调用函数 FUNCALL and APPLY
(foo 1 2 3) == (funcall #’foo 1 2 3)
然而,当实参列表只在运行期时,FUNCALL的表现不佳。
(foo (first plot-data) (second plot-data) (third plot-data) (fourth plot-data))
这样的写法令人讨厌
这就是需要APPLY的原因了
Apply 的第一个参数是一个函数对象,在这之后是一个列表
(apply #’foo plot-data)
Apply还接受孤立的实参,只要最后一个参数是一个列表
(apply #’foo #’exp plot-data)
六:匿名函数 (LAMBDA)
(lambda (parameters) body)
(funcall #’(lambda (x y) (+ x y)) 2 3) -> 5
甚至还可以将LAMBDA表达式用作函数名
((lambda (x y) (+ x y)) 2 3) -> 5
(defun double(x) (* 2 x))
(foo #’double 1 2)不如(foo #’(lambda (x) (* 2 x)) 1 2)清晰简单
LAMBDA还有闭包作用