LISP - `:after` 方法组合
Lisp - :after 方法组合
Section titled “Lisp - :after 方法组合”Common Lisp 对象系统 (CLOS) 提供了一个强大的特性,称为方法组合 (method combination),它控制着泛型函数的不同方法如何组合和执行。标准方法组合使用 :after 等限定符来定义辅助方法,这些方法在主方法之外运行。可以将它们看作“钩子”或“中间件”,让您能够在主逻辑之后可靠地执行代码。
:after 方法限定符特别适用于:
- 资源清理:确保文件或网络连接在使用后关闭。
- 日志记录和审计:记录某个操作已完成。
- 状态更新:在数据更改后刷新缓存或更新用户界面。
定义 :after 方法
Section titled “定义 :after 方法”您可以使用 defmethod 定义 :after 方法,将 :after 限定符放在泛型函数名称和参数列表之间。:after 方法的返回值始终被忽略。
;; 1. 定义一个泛型函数。(defgeneric process-data (data))
;; 2. 定义一个 :after 方法。;; 此方法将在 `process-data` 的任何适用主方法 *之后* 运行。(defmethod process-data :after ((data t)) (format t "[LOG] Finished processing data: ~a~%" data))
;; 3. 定义另一个更具体的 :after 方法。(defmethod process-data :after ((data integer)) (format t "[CLEANUP] Integer-specific cleanup complete.~%"))当调用泛型函数时,CLOS 会按特定顺序运行适用的方法:
- 最具体的
:around方法(如果有)。 - 所有适用的
:before方法,从最具体到最不具体。 - 仅运行最具体的**主 (primary)**方法。
- 所有适用的
:after方法,从最不具体到最具体。
实际示例::after 的应用
Section titled “实际示例::after 的应用”让我们来看一个完整的示例,演示调用顺序和用例。
main.lisp
Section titled “main.lisp”;; 1. 泛型函数定义(defgeneric process-data (data))
;; 2. 辅助的 :after 方法
;; 这是最不具体的 :after 方法(适用于所有 `t` 类型)(defmethod process-data :after ((data t)) (format t "--> 调用的 T 的 :after 方法(最不具体)。~%"))
;; 这是一个更具体的 :after 方法(仅适用于整数)(defmethod process-data :after ((data integer)) (format t "--> 调用的整数的 :after 方法(最具体)。~%"))
;; 3. 主方法(主要逻辑)
;; 整数的主方法(defmethod process-data ((data integer)) (format t "正在运行整数的主方法。~%") (* data 2)) ; 主方法的返回值是整个调用的返回值。
;; 字符串的主方法(defmethod process-data ((data string)) (format t "正在运行字符串的主方法。~%") (concatenate 'string data "-processed"))
;; --- 函数调用 ---(format t "--- 调用 (process-data 10) ---~%")(process-data 10)
(format t "~%--- 调用 (process-data \"test\") ---~%")(process-data "test")当您执行代码时,您将看到精确的执行顺序:
--- Calling (process-data 10) ---Running PRIMARY method for INTEGER.--> :after method for T (least specific) called.--> :after method for INTEGER (most specific) called.
--- Calling (process-data "test") ---Running PRIMARY method for STRING.--> :after method for T (least specific) called.- 当调用
(process-data 10)时:整数的主方法首先运行。然后,执行:after方法,从最不具体 (t) 的开始,到最具体 (integer) 的结束。 - 当调用
(process-data "test")时:字符串的主方法运行。之后,只有t的:after方法适用,因此它会运行。 - 返回值:请注意,
:after方法的返回值会被丢弃。泛型函数调用的最终返回值始终是主方法返回的值。
如果您不确定哪些方法适用于某个泛型函数,您可以在 REPL 中使用 describe:
;; 在您的 REPL 中,定义方法后:CL-USER> (describe 'process-data)
;; 输出将列出所有定义的方法及其限定符。