Skip to content

LISP - 函数

函数是任何 Lisp 程序的基本构建块。它们是执行特定任务的独立代码单元。在 Lisp 中,函数是头等公民,这意味着它们可以像数字或字符串等其他数据类型一样,存储在变量中,作为参数传递给其他函数,并作为值返回。

defun 宏用于定义具名函数。其基本语法如下:

(defun function-name (parameter-list)
"Optional documentation string."
function-body)
  • function-name:一个用于命名函数的符号。
  • parameter-list:一个用于函数参数的符号列表。
  • documentation string:描述函数功能的可选字符串。这是一个最佳实践,可以通过 (documentation 'function-name 'function) 获取。
  • function-body:一个或多个 Lisp 表达式。最后求值的表达式的值将作为函数的返回值隐式返回。

让我们定义一个计算圆面积的函数。注意内置常量 pi 的使用。

(defun circle-area (radius)
"Calculates the area of a circle given its radius."
(* pi radius radius))
;; 调用函数并打印结果
(format t "Area of a circle with radius 10: ~f~%" (circle-area 10))
Area of a circle with radius 10: 314.15927

Lisp 函数使用 lambda 列表关键字支持高度灵活的参数列表。

  • &optional:用于可带默认值的可选参数。
  • &rest:用于可接受任意数量额外参数的可变参数函数。
  • &key:用于通过名称而不是位置指定的参数。
(defun greet (name &key (greeting "Hello") (punctuation "!"))
"Greets a person with customizable options."
(format nil "~a, ~a~a" greeting name punctuation))
;; --- 用法 ---
;; 默认参数
(print (greet "Alice"))
;; 覆盖一个关键字参数
(print (greet "Bob" :greeting "Hi"))
;; 覆盖所有关键字参数(顺序无关)
(print (greet "Charlie" :punctuation "." :greeting "Good morning"))
"Hello, Alice!"
"Hi, Bob!"
"Good morning, Charlie."

如前所述,函数会隐式返回其最后求值表达式的值。要从块内部(例如循环)提前返回,可以使用 return-from。

示例:使用 return-from 进行显式返回

Section titled “示例:使用 return-from 进行显式返回”
(defun find-first-odd (numbers)
"Returns the first odd number in a list, or NIL if none are found."
(dolist (num numbers) ; dolist 隐式创建一个名为 nil 的块
(when (oddp num)
(return-from find-first-odd num)))
;; 只有当循环完成且没有找到奇数时,才会到达此部分。
nil)
(print (find-first-odd '(2 4 5 6 7)))
(print (find-first-odd '(2 4 6 8)))
5
NIL

defun 宏会自动将函数体包装在一个与函数同名的块中。(return-from find-first-odd num) 会立即退出 find-first-odd 函数并返回 num 的值。

高阶函数是接受另一个函数作为参数或返回一个函数的函数。这是函数式编程的基石。

可以使用 lambda 特殊形式动态创建匿名(无名)函数。

mapcar 是一个经典的高阶函数。它将给定函数应用于列表的每个元素,并返回一个包含结果的新列表。

;; 定义一个数字列表
(defparameter *numbers* '(1 2 3 4 5))
;; 使用 mapcar 和具名函数 (sqrt)
(print (mapcar #'sqrt *numbers*))
;; 使用 mapcar 和匿名 lambda 函数来求每个数的平方
(print (mapcar (lambda (x) (* x x)) *numbers*))
(1.0 1.4142135 1.7320508 2.0 2.236068)
(1 4 9 16 25)
  • #'sqrt:#'(尖引号)语法是 (function sqrt) 的简写。它获取与符号 sqrt 相关联的函数对象。
  • (lambda (x) (* x x)):创建一个接受一个参数 x 并返回其平方的函数。这个函数对象直接传递给 mapcar。