LISP - `:primary` 方法组合
Lisp - 理解方法组合
Section titled “Lisp - 理解方法组合”在 Common Lisp 对象系统(CLOS)中,**方法组合(method combination)**定义了将泛化函数调用(generic function call)的所有适用方法组合成一个有效方法(effective method)的过程。**标准方法组合(Standard Method Combination)**是默认且最常用的类型。它根据方法的角色(通过*限定符(qualifiers)*指定)来协调方法的执行。
方法最基本的类型是主方法(primary method)。这些方法在定义时没有使用任何限定符。在标准组合中,CLOS 会调用单个最具体的主方法。该方法负责提供主要逻辑并返回泛化函数调用的最终值。
- 泛化函数(
defgeneric):定义函数的名称和参数列表,充当一个分发器。它也可以指定方法组合类型,但standard是默认值。 - 方法(
defmethod):为泛化函数针对特定的参数类型(类)提供具体的实现。 - 主方法(Primary Method):一个没有限定符的方法。它构成核心逻辑。在默认的
standard组合中,只有最具体的一个会被执行。 - 辅助方法(Auxiliary Methods):带有
:before、:after和:around等限定符的方法。它们增强了主方法的行为。本教程只关注主方法。
定义泛化函数和主方法
Section titled “定义泛化函数和主方法”首先,我们定义一个泛化函数。然后,我们为不同的数据类型提供不同的方法实现。
;; 定义一个名为 'combine' 的泛化函数,它接受两个参数。;; 默认情况下,它使用标准方法组合。(defgeneric combine (x y))
;; 为 'combine' 泛化函数定义一个主方法。;; 此方法专门处理 'x' 和 'y' 都为 'number' 类型的情况。(defmethod combine ((x number) (y number)) "此主方法处理两个参数都是数字的情况。" (+ x y))
;; 为 'string' 类型定义另一个主方法。(defmethod combine ((x string) (y string)) "此主方法处理两个参数都是字符串的情况。" (concatenate 'string x y))当 combine 被调用时,CLOS 会检查参数的类型,并将调用分派给最具体的匹配方法。
让我们创建一个完整可运行的示例,演示如何根据参数类型调用不同的主方法。
main.lisp
Section titled “main.lisp”;; 现代 Lisp 开发通常从定义一个包开始。(defpackage #:method-combination-example (:use #:cl))(in-package #:method-combination-example)
;; 1. 定义一个泛化函数。(defgeneric combine (x y) (:documentation "根据对象的类型组合两个对象。"))
;; 2. 定义一个针对数字的主方法。(defmethod combine ((x number) (y number)) "当组合两个数字时,返回它们的和。" (+ x y))
;; 3. 定义一个针对字符串的主方法。(defmethod combine ((x string) (y string)) "当组合两个字符串时,返回它们的拼接结果。" (concatenate 'string x y))
;; 4. 定义一个针对列表的主方法。(defmethod combine ((x list) (y list)) "当组合两个列表时,将它们追加。" (append x y))
;; --- 演示分派机制 ---(format t "Combining numbers 10 and 20: ~a~%" (combine 10 20))(format t "Combining strings \"Hello\" and \" World!\": ~a~%" (combine "Hello" " World!"))(format t "Combining lists '(1 2) and '(3 4): ~a~%" (combine '(1 2) '(3 4)))Combining numbers 10 and 20: 30Combining strings "Hello" and " World!": Hello World!Combining lists '(1 2) and '(3 4): (1 2 3 4)当泛化函数 combine 被调用时,Common Lisp 运行时会检查所提供参数的类型,并选择最具体的主方法来执行:
(combine 10 20):两个参数都是number类型。调用了针对数字的特化方法,返回它们的和,即30。(combine "Hello" " World!"):两个参数都是string类型。调用了针对字符串的特化方法,返回拼接后的字符串,即"Hello World!"。(combine '(1 2) '(3 4)):两个参数都是list类型。调用了针对列表的特化方法,返回追加后的列表,即(1 2 3 4)。
尽管主方法是主力,但方法组合的真正强大之处在于使用辅助方法(:before、:after、:around)来创建复杂、分层的行为。例如,你可以添加一个 :around 方法来记录 combine 的每次调用,无论参数类型如何。