Common lisp 如何使用方法组合减少代码重复,同时保持可能的早期返回

Common lisp 如何使用方法组合减少代码重复,同时保持可能的早期返回,common-lisp,clos,Common Lisp,Clos,我得到了一组表示必须处理的消息的类。但对于处理者来说,只有有限的开放空间。因此,处理消息对象的处理程序的任何“分派”都必须首先检查是否有空闲点 如果有->调度 如果没有->不发送并返回相应的消息 由于这部分代码在任何分派方法中都是相同的,所以我认为最好使用方法组合工具来实现这一点,但我不知道如何实现 在我当前的代码库中,我尝试使用:before方法,但显然不能在这样的上下文中使用return: (defclass message () ((msg :initarg :msg :reader ms

我得到了一组表示必须处理的消息的类。但对于处理者来说,只有有限的开放空间。因此,处理消息对象的处理程序的任何“分派”都必须首先检查是否有空闲点

如果有->调度

如果没有->不发送并返回相应的消息

由于这部分代码在任何分派方法中都是相同的,所以我认为最好使用方法组合工具来实现这一点,但我不知道如何实现

在我当前的代码库中,我尝试使用:before方法,但显然不能在这样的上下文中使用return:

(defclass message () ((msg :initarg :msg :reader msg)))

(defclass message-ext (message) 
    ((univ-time :initarg :univ-time :reader univ-time)))

(defparameter *open-handler* nil)

(defgeneric handle (message)
  (:documentation "handle the given message appropriately"))

(defmethod handle :before ((message message))
  (when (> (length *open-handler*) 1)
    (return :full)))

(defmethod handle ((message message))
  (push (FORMAT nil "dispatched handler") *open-handler*))

(defmethod handle ((message-ext message-ext))
  (push (FORMAT nil "dispatched ext handler") *open-handler*))

(handle (make-instance 'message :msg "allemeineentchen"))

(handle (make-instance 'message-ext 
                       :msg "rowrowrowyourboat" 
                       :univ-time (get-universal-time)))

(handle (make-instance 'message-ext 
                       :msg "gentlydownthestreet" 
                       :univ-time (get-universal-time)))

Execution of a form compiled with errors.
Form:
  (RETURN-FROM NIL FULL)
Compile-time error:
  return for unknown block: NIL
   [Condition of type SB-INT:COMPILED-PROGRAM-ERROR]

Restarts:
 0: [RETRY] Retry SLIME interactive evaluation request.
 1: [*ABORT] Return to SLIME's top level.
 2: [TERMINATE-THREAD] Terminate this thread (#<THREAD "worker" RUNNING {100594F743}>)

Backtrace:
  0: ((SB-PCL::FAST-METHOD HANDLE :BEFORE (MESSAGE)) #<unavailable argument> #<unavailable argument> #<unavailable argument>)
  1: ((SB-PCL::EMF HANDLE) #<unavailable argument> #<unavailable argument> #<MESSAGE-EXT {1005961733}>)
  2: (SB-INT:SIMPLE-EVAL-IN-LEXENV (HANDLE (MAKE-INSTANCE 'MESSAGE-EXT :MSG "gentlydownthestreet" :UNIV-TIME (GET-UNIVERSAL-TIME))) #<NULL-LEXENV>)
  3: (EVAL (HANDLE (MAKE-INSTANCE 'MESSAGE-EXT :MSG "gentlydownthestreet" :UNIV-TIME (GET-UNIVERSAL-TIME))))
  4: ((LAMBDA () :IN SWANK:INTERACTIVE-EVAL))
(defclass消息()((msg:initarg:msg:reader-msg)))
(defclass消息分机(消息)
((大学时间:initarg:univ时间:reader univ时间)))
(defparameter*打开处理程序*nil)
(通用句柄(消息)
(:文档“适当处理给定消息”))
(defmethod句柄:在((消息))之前)
(当(>(长度*打开处理程序*)1)
(返回:完整)
(defmethod句柄((消息))
(推送(格式为nil“已调度处理程序”)*打开处理程序*)
(defmethod句柄((message ext message ext))
(推送(格式为nil“已调度的外部处理程序”)*打开处理程序*)
(句柄(生成实例的消息:msg“alleminentchen”))
(句柄(生成实例)消息分机
:msg“RowYourBoat”
:大学时间(获取世界时间)
(句柄(生成实例)消息分机
:msg“优雅地走下街道”
:大学时间(获取世界时间)
执行带错误编译的表单。
表格:
(从零到全返回)
编译时错误:
返回未知块:NIL
[SB-INT类型的条件:编译程序错误]
重新启动:
0:[重试]重试SLIME交互式评估请求。
1:[*中止]返回SLIME的顶层。
2:[终止线程]终止此线程(#)
回溯:
0:((SB-PCL::快速方法句柄:在(消息)之前)#
1:((SB-PCL::EMF句柄)####)
2:(SB-INT:SIMPLE-EVAL-IN-lexev(HANDLE(MAKE-INSTANCE'MESSAGE-EXT:MSG“gentlydowthestreet”:UNIV-TIME(GET-UNIVERSAL-TIME))#
3:(EVAL(HANDLE(MAKE-INSTANCE'MESSAGE-EXT:MSG“gentlydowthestreet”:UNIV-TIME(GET-UNIVERSAL-TIME)))
4:((LAMBDA():在SWANK中:INTERACTIVE-EVAL))

这种方法是否明智?如果是,我如何以工作方式做到这一点?(我已经尝试了
返回,结果相同)

我认为您应该使用
:around
方法限定符:

(defmethod handle :around ((message message))
  (if (cddr *open-handler*)
      :full
      (call-next-method)))
然而,一种更为“lispy”的方法是使用,例如:

(define-condition too-many-messages (...) (...) ...)
(defun add-message (message)
  (when (cddr *open-handler*)
    (signal 'too-many-messages))
  (push message *open-handler*))
(defmethod handle ((message message))
  (add-message (FORMAT nil "dispatched handler")))
除了检查
handle
函数的返回值外,还必须处理条件(例如使用)

注:在列表上调用
length
来检查它是否足够长不是一个好主意-尽管在您的情况下,当列表保证很短时,这可能更像是一个样式问题


PPS。使用单词
handle
作为函数名不是一个很好的主意,因为CL有包含它的函数(例如,
handler case
)。这将使代码中的搜索复杂化,还会使阅读代码的人感到困惑。

您不能调用
RETURN
从这样的函数返回

您需要在函数名中使用
RETURN-FROM
。但在这里,它将从方法返回,而不是泛型函数

@sds有一个答案。另一种方法是向用户定义的条件发送信号,然后在其他地方处理它。使用了较旧的代码
catch
throw

更复杂的任务是用户定义的方法组合