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