Common Lisp笔记(3)函数定义

来源:互联网 发布:php获取访客ip 编辑:程序博客网 时间:2024/05/11 00:43

函数一般使用 DEFUN 定义

(defun name (parameter*)    "Optional documentation string."    body-form*)

name
任何符号都可以作为函数名,通常函数名仅包含字典字符和连字符,但是在特定命名约束里,其他字符也允许使用。
Lisp 的风格是使用连字符也不是下划线,例如 frob-widget 比 frob_widget 或者 frobWidget 更具有 Lisp 风格。

parameter* 函数形参列表,因为是动态语言,没有类型声明。
除了必要形参以外,一个函数还可以有可选形参,剩余形参和关键字形参。

  • 可选形参
    可选形参适用于一些较为分散并且不能确定调用者是否会提供值得形参。
    在必要形参的名字之后放置符号 &optional ,后面的名字就是可选形参,
    例如:(defun foo (a b &optional c d) (list a b c d)
    c 和 d 就是可选形参,函数传值的时候,先给必要形参赋值,如果实参还没有用完,再依次赋值给可选形参,剩余的如果没有设置默认值自动绑定到 nil ,如果设置了默认值,则绑定到默认值。
    设置默认值使用 () 将名字和默认值括起来,例如: (defun make-retangle (width &optional (height width)) ...) , 如果只有一个值穿进去,则绑定到 width ,同时 height 默认取和 width 同样的值。
    有时需要知道一个可选形参的值是否为默认值,可以在形参标识符的默认值表达式之后添加一个变量来做到这一点,该变量将在调用者实际为该形参提供一个实参的时候绑定真值,否则为 nil 。约定这个变量是对应的形参加个 -supplied-p 后缀。
    例如:
    (defun foo (a b &optional (c 3 c-supplied-p)) (list a b c c-supplied-p)
  • 剩余形参
    某些函数需要接受可变数量的实参。例如 format 有两个必要形参,流和控制串。但在这两个参数后面,它还需要一组可变数量的实参,这取决于控制串需要插入过少个值。 + 函数可以接受可变数量的形参。
(format t "hello,world")(format t "x: ~d y: ~d" x y)(+)(+ 1)(+ 1 2 3)

&rest Lisp 允许在符号 &rest 之后包括很多形参。如果函数带有 &rest 形参,那么任何满足了必要和可选形参之后的其余所有实参就将被收集到一个列表里成为该 &rest 形参的值。format 和 + 的形参列表可能看起来像这个样子。

(defun format (stream string &rest values) ...)(defun + (&rest numbers) ...)
  • 关键字形参
    关键字形参可以使实参按照关键字绑定到形参上,所以更具备定制性,可以满足更高的函数定义要求。在任何必要的 &optional 和 &rest 形参在之后,可以加上符号 &key 以及任意数量的关键字形参标识符,后者的格式类似于可选形参标识符。
    例如:(defun foo (&key a b c) (list a b c))
    当调用这个函数时,每一个关键字形参被绑定到紧跟在同名关键字后面的那个值上,没有的被绑定为 nil 。例如:(-之后为函数调用结果)
(foo)   -(nil nil nil)(foo :a 1)   -(1 nil nil)(foo :a 1 :c 3)   -(1 nil 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 :charlie 30) 结果:(10 0 30 T)

document string
描述此函数的字符串,可以通过 documentation 函数获取
body-form
函数主题部分。
函数返回值
默认情况下,函数的最后一个表达式的值作为整个函数的返回值。如果需要在函数中间返回,可以用 return-from 特殊操作符。也就是很多语言中的 return 。例如下面这个函数使用嵌套循环来发现一对在10以内,并且乘机大于 n 的数对:

(defun foo (n)    (dotimes (i 10)        (dotimes (j 10)            (when (> (* i j) n)                (return-from foo (list i j))))))

return-from 后面必须要指定返回函数名,这多少有些麻烦,但事实上,显示的 return-from 调用在 Lisp 中的频率远小于其他语言算法中使用 return 的频率。

0 0
原创粉丝点击