Skip to content

LISP - `append` 方法组合

方法组合是 Common Lisp 对象系统 (CLOS) 的基石,它允许开发人员定义层次结构中不同类的方法应如何协同工作。虽然标准方法组合 (standard method combination)(:around、:before、:after)最为常见,但 CLOS 也提供了其他内置类型用于特殊任务。本教程重点介绍 append 方法组合,它用于将多个方法的结果收集到一个列表中。

当您为泛型函数指定 :method-combination append 时,CLOS 会改变其执行适用方法的方式。与标准层次结构不同,它会执行以下操作:

  1. 它调用给定一组参数的所有适用的主方法。
  2. 它期望每个方法都返回一个列表。
  3. 它使用 append 函数将所有这些列表连接起来,以生成最终结果。

这对于系统中不同部分需要向集合贡献项的场景非常有用,例如收集权限、功能或配置值。

让我们来建模一个系统,其中用户可以拥有多个角色,并且每个角色授予一组权限。我们希望有一个函数 get-permissions,它返回用户所有权限的完整列表。

(defclass user ()
())
(defclass guest (user)
())
(defclass editor (user)
())
(defclass administrator (editor) ; An admin is also an editor
())

步骤 2:使用 append 定义泛型函数

Section titled “步骤 2:使用 append 定义泛型函数”
;; 定义一个将使用 'append' 方法组合的泛型函数。
(defgeneric get-permissions (user)
(:method-combination append))

每个方法都针对用户类进行了特殊化,并返回与该角色关联的权限列表。请注意 defmethod 中函数名后面的 append 关键字。

;; 每个用户,甚至是访客,都可以查看内容。
(defmethod get-permissions append ((user guest))
(list :view-content))
;; 编辑者也可以创建和编辑帖子。
(defmethod get-permissions append ((user editor))
(list :create-post :edit-post))
;; 管理员拥有最高级别的权限。
(defmethod get-permissions append ((user administrator))
(list :delete-user :manage-system))

由于多重继承和方法组合,对类的实例调用 get-permissions 将触发其层次结构中所有相关方法,并且 append 将收集结果。

;; 创建我们的用户类型实例
(let ((guest-user (make-instance 'guest))
(editor-user (make-instance 'editor))
(admin-user (make-instance 'administrator)))
(format t "访客权限:~s~%" (get-permissions guest-user))
(format t "编辑者权限:~s~%" (get-permissions editor-user))
(format t "管理员权限:~s~%" (get-permissions admin-user)))
Guest permissions: (:VIEW-CONTENT)
Editor permissions: (:CREATE-POST :EDIT-POST :VIEW-CONTENT)
Admin permissions: (:DELETE-USER :MANAGE-SYSTEM :CREATE-POST :EDIT-POST :VIEW-CONTENT)

管理员的输出展示了这种技术的强大之处。一个 administrator 继承自 editor,而 editor 又继承自 user(在我们的设置中隐式地继承自 guest)。CLOS 会调用所有三个类的相应方法,并且 append 组合巧妙地将所有权限收集到一个综合列表中。结果的顺序取决于类优先级列表 (Class Precedence List, CPL),该列表默认将最具体的类的结果放在前面。

CLOS 还提供了其他有用的方法组合,例如 +(对数值结果求和)、and(遇到 nil 短路)、or(遇到非 nil 短路)和 list(将结果收集到列表的列表中)。选择正确的方法组合类型可以大大简化您的代码。