Skip to content

LISP - 使用 `funcall` 调用函数

Lisp - 使用 funcall 进行动态函数调用

Section titled “Lisp - 使用 funcall 进行动态函数调用”

在 Common Lisp 中,函数是一等公民。这意味着它们可以存储在变量中,作为参数传递给其他函数,并作为结果返回。funcall 函数是一个基本工具,允许你在运行时调用一个函数对象。这对于编写高阶函数和实现动态行为至关重要。

(funcall function-designator arg1 arg2 ...)

其中:

  • function-designator:这是要调用的函数。它可以是一个命名函数的符号(例如 ’+、‘my-function)、一个lambda 表达式(一个匿名函数),或者一个函数对象本身。
  • arg1、arg2、…:这些是要传递给指定函数的参数。

专业提示:要从一个名称获取函数对象,请使用特殊的 function 操作符,或其读取器宏(reader macro)的简写形式 #‘。例如,(function +) 与 #’+ 是相同的。

你可以使用符号按名称动态调用函数。

;;; Define a simple function
(defun multiply-by-two (x)
(* x 2))
;; Call a built-in function using its symbol name
(print (funcall '+ 2 3))
;; Call our custom function using its symbol name
(print (funcall 'multiply-by-two 5))

执行代码会产生以下结果:

5
10

这展示了一等函数的强大之处,即要执行的操作可以在运行时决定。

(defun multiply-by-two (x) (* x 2))
(defun add-ten (x) (+ x 10))
;; Use LET to bind a function object to a local variable
(let ((operation #'*))
(print (funcall operation 4 5))) ; Evaluates to 20
(let ((operation #'add-ten))
(print (funcall operation 6))) ; Evaluates to 16
20
16

funcall 可以直接调用使用 lambda 创建的匿名函数。

;; Call a lambda expression that multiplies two numbers
(print (funcall #'(lambda (x y) (* x y)) 3 4))
12

高阶函数是接受另一个函数作为参数的函数。funcall 使其易于实现。

;; Define a higher-order function that applies an operation to two arguments
(defun apply-operation (operation arg1 arg2)
(funcall operation arg1 arg2))
;; Use the higher-order function with different operations
(print (apply-operation #'+ 10 5))
(print (apply-operation #'* 10 5))
(print (apply-operation #'(lambda (a b) (- a b)) 10 5))
15
50
5

一个常见错误是尝试 funcall 一个未定义为函数的符号。Lisp 系统会发出 undefined-function 错误信号。

另外,请注意 apply 函数。它与 funcall 类似,但其最后一个参数必须是被调用函数的参数列表。

(funcall #'+ 1 2 3) ; 将 1, 2, 3 作为三个单独的参数传递
(apply #'+ '(1 2 3)) ; 将列表 (1 2 3) 的元素作为参数传递
(apply #'+ 1 2 '(3 4)) ; apply 也可以接受前置参数
  • 动态分派:funcall 是当具体函数在运行时才知道时,调用函数的主要机制。
  • 一等函数:funcall 完全体现了 Lisp 将函数作为数据处理的特性,从而实现了强大的函数式编程模式。
  • 高阶函数:它是创建接受其他函数作为参数的函数的关键,也是现代软件设计的基石。