在 Lisp 中,函数分两种:有名函数和匿名函数(lambda函数)。


有名函数 defun

有名函数的标准定义格式为:

  1. (defun <name> (list of arguments)
  2. "docstring"
  3. (function body))

在函数中,返回值是函数主体中的最后一个表达式的结果。与大部分语言不同的是,lisp 中的函数没有 “return xx” 这样的语句用来声明返回值。例如:

  1. (defun hello-world ()
  2. ;;
  3. (print "hello world!"))

调用 hello-world 函数:

  1. (hello-world)
  2. ;; "hello world!" <-- output
  3. ;; "hello world!" <-- a string is returned.

参数

必需参数

与大部分语言相同,函数中需要定义一些必需参数:

  1. (defun hello (name)
  2. "Say hello to `name'."
  3. (format t "hello ~a !~&" name))
  4. ;; HELLO

调用 hello

  1. (hello "me")
  2. ;; hello me ! <-- this is printed by `format`
  3. ;; NIL <-- return value: `format t` prints a string to standard output and returns nil.

可选参数:&optional

可选参数定义在 &optional 这个关键词后面,且这些参数是有序的。如:

  1. (defun hello (name &optional age gender) ...)

调用时需要这样调用:

  1. (hello "me") ;; a value for the required argument, zero optional arguments
  2. (hello "me" "7") ;; a value for age
  3. (hello "me" 7 :h) ;; a value for age and gender

关键词参数:&key

通常情况下,要记住函数中参数的顺序很不方便,所以就引入了关键词参数 &key <name>。通过 :name <value> 这样的方式来传递参数。如果关键词 name 的值没有设置的话,默认为 nil

  1. (defun hello (name &key happy)
  2. "If `happy' is `t', print a smiley"
  3. (format t "hello ~a " name)
  4. (when happy
  5. (format t ":)~&"))

所以,我们可以这样调用 hello

  1. (hello "me")
  2. (hello "me" :happy t)
  3. (hello "me" :happy nil) ;; useless, equivalent to (hello "me")

但是 (hello "me" :happy) 是非法的。

注:在关键词参数中,如果调用其他的关键词时,会报错,这是可以通过 &allow-other-keys 来修复。

  1. (defun hello (name &key happy)
  2. (format t "hello ~a~&" name))
  3. (hello "me" :lisper t)
  4. ;; => Error: unknow keyword argument
  1. (defun hello (name &key happy &allow-other-keys)
  2. (format t "hello ~a~&" name))
  3. (hello "me" :lisper t)
  4. ;; hello me

默认参数

有时,需要将函数的某个参数设置一个默认值,这样 hello 就可以这样定义

  1. (defun hello (name &key (happy t)) ...)

这样,调用 hellohappy 的值默认就是 t 了。

不定参数:&rest

在不确定参数的个数时,可以使用 &rest <variable> 这样的方式来定义,其中 &rest 后面的参数会当作一个 list 来处理。

  1. (defun mean (x &rest numbers)
  2. (/ (apply #'+ x numbers)
  3. (1+ (length numbers))))
  1. (mean 1)
  2. (mean 1 2)
  3. (mean 1 2 3 4 5)

返回值

在 Lisp 中,函数的返回值就是函数主体中最后一个表达式执行的结果。也有非标准的 return-from <function name> <value> 这样的语句,但是大部分情况下用不到。同时,Common Lisp 支持返回多个值。

返回多个值有三个关键词:valuesmultiple-value-bindnth-value

返回多个值不是将所有的结果都放入一个元组或列表中,这是很常见的概念混淆。

values

  1. (defun foo (a b c)
  2. a)
  3. (foo :a :b :c)
  4. ;; :A
  5. (defvar *res* (foo :a :b :c))
  6. ;; :A
  1. (defun foo (a b c)
  2. (values a b c))
  3. (foo :a :b :c)
  4. ;; :A
  5. ;; :B
  6. ;; :C
  7. (setf *res* (foo :a :b :c))
  8. ;; :A

从上面代码可以看出,如果 foo 返回的是一个列表的话,那么 res 的值将会是 (:a :b :c) 这样一个列表,而不是 :A 这个值。

multiple-value-list

该关键词的作用是将返回的多个值组合成一个列表

  1. (multiple-value-list (values 1 2 3))
  2. ;; (1 2 3)

values-list

values-listmultiple-value-list 相反,它返回的是列表中的每个元素

  1. (values-list '(1 2 3))
  2. ;; 1
  3. ;; 2
  4. ;; 3

匿名函数 lambda

匿名函数的声明如下:

  1. (lambda (x) (print x))

匿名函数的调用:

  1. ((lambda (x) (print x)) "hello")
  2. ;; hello

funcallapply

  1. (funcall #'+ 1 2)
  2. (apply #'+ '(1 2))

返回函数的函数

  1. (defun adder (n)
  2. (lambda (x) (+ x n)))
  3. ;; ADDER
  4. (adder 5)
  5. ;; #<CLOSURE (LAMBDA (X) :IN ADDER) {100994ACDB}>
  6. (funcall (adder 5) 3)
  7. ;; 8

上面示例中,(adder 5) 返回的是一个匿名函数。但是需要使用 funcall 关键词来调用,不能想正常函数调用来调用。

  1. ((adder 3) 5)
  2. In: (ADDER 3) 5
  3. ((ADDER 3) 5)
  4. Error: Illegal

Common Lisp 中提供了两个函数来查看变量或函数是否赋值/绑定:boundpfboundp

  1. ;; The symbol foo is bound to nothing:
  2. CL-USER> (boundp 'foo)
  3. NIL
  4. CL-USER> (fboundp 'foo)
  5. NIL
  6. ;; We create a variable:
  7. CL-USER> (defparameter foo 42)
  8. FOO
  9. * foo
  10. 42
  11. ;; Now foo is "bound":
  12. CL-USER> (boundp 'foo)
  13. T
  14. ;; but still not as a function:
  15. CL-USER> (fboundp 'foo)
  16. NIL
  17. ;; So let's define a function:
  18. CL-USER> (defun foo (x) (* x x))
  19. FOO
  20. ;; Now the symbol foo is bound as a function too:
  21. CL-USER> (fboundp 'foo)
  22. T
  23. ;; Get the function:
  24. CL-USER> (function foo)
  25. #<FUNCTION FOO>
  26. ;; and the shorthand notation:
  27. * #'foo
  28. #<FUNCTION FOO>
  29. ;; We call it:
  30. (funcall (function adder) 5)
  31. #<CLOSURE (LAMBDA (X) :IN ADDER) {100991761B}>
  32. ;; and call the lambda:
  33. (funcall (funcall (function adder) 5) 3)
  34. 8

注:在 Lisp 中,变量名和函数名可以相同,因为 Common Lisp 中变量和函数并不是存储在一起的,而是分开存储的。

闭包(Closure)

闭包,就是让一个函数可以使用一个 词法绑定(lexcial bindings)On Lisp 中的定义为:函数和一组变量的绑定的组合(a combination of a function and a set of variable bindings)。 Let Over Lambda中对闭包的解读为:一个保存了词法的环境(a saved lexical environment)。 可以将闭包理解为 C 语言中的 结构体(struct)或者面向对象语言(Java/C++)中的 类(class)

  1. (let ((limit 3)
  2. (counter -1))
  3. (defun my-counter ()
  4. (if (< counter limit)
  5. (incf counter)
  6. (setf counter 0))))
  7. (my-counter)
  8. 0
  9. (my-counter)
  10. 1
  11. (my-counter)
  12. 2
  13. (my-counter)
  14. 3
  15. (my-counter)
  16. 0

类似的

  1. (defun repeater (n)
  2. (let ((counter -1))
  3. (lambda ()
  4. (if (< counter n)
  5. (incf counter)
  6. (setf counter 0)))))
  7. (defparameter *my-repeater* (repeater 3))
  8. ;; *MY-REPEATER*
  9. (funcall *my-repeater*)
  10. 0
  11. (funcall *my-repeater*)
  12. 1
  13. (funcall *my-repeater*)
  14. 2
  15. (funcall *my-repeater*)
  16. 3
  17. (funcall *my-repeater*)
  18. 0

Currying

Concept

A related concept is that of currying which you might be familiar with if you’re coming from a functional language. After we’ve read the last section that’s rather easy to implement:

  1. CL-USER> (defun curry (function &rest args)
  2. (lambda (&rest more-args)
  3. (apply function (append args more-args))))
  4. CURRY
  5. CL-USER> (funcall (curry #'+ 3) 5)
  6. 8
  7. CL-USER> (funcall (curry #'+ 3) 6)
  8. 9
  9. CL-USER> (setf (symbol-function 'power-of-ten) (curry #'expt 10))
  10. #<Interpreted Function "LAMBDA (FUNCTION &REST ARGS)" {482DB969}>
  11. CL-USER> (power-of-ten 3)
  12. 1000

With the Alexandria library

Now that you know how to do it, you may appreciate using the
implementation of the
Alexandria
library (in Quicklisp).

  1. (ql:quickload :alexandria)
  2. (defun adder (foo bar)
  3. "Add the two arguments."
  4. (+ foo bar))
  5. (defvar add-one (alexandria:curry #'adder 1) "Add 1 to the argument.")
  6. (funcall add-one 10) ;; => 11
  7. (setf (symbol-function 'add-one) add-one)
  8. (add-one 10) ;; => 11

Documentation