Hos*_*ork 5 lisp common-lisp clos generic-function
泛型似乎提供了一个很好的工具来提取一个常用词并让它根据你传递的类型对事物起作用,事后可扩展性.
但是,已经采用并且没有定义为通用的常用词呢?如果我尝试定义REMOVE,例如:
(defclass reticulator () (splines))
(defmethod remove ((item reticulator)
sequence &key from-end test test-not start end count key))
Run Code Online (Sandbox Code Playgroud)
我在SBCL中收到错误:
COMMON-LISP:REMOVE已经命名普通函数或宏.
这些内置函数之一是否有成语或通用的"通用ify"方式?人们这样做吗?
只是为了看看会发生什么我尝试使用泛型覆盖REMOVE:
(defgeneric remove (item sequence &key from-end test test-not start end count key))
Run Code Online (Sandbox Code Playgroud)
警告:重新定义COMMON-LISP:DEFGENERIC中的REMOVE
我不知道是否有一个"好"的方法,这将导致旧的实现,允许重载特定类型,希望赋予新的含义.
Rai*_*wig 14
为什么它们是这样的?
Common Lisp的第一个版本是从1981/82开始设计的,结果发表于1984年的Common Lisp the Language一书.该语言本身主要是基于Lisp Machine Lisp(又名Zetalisp)设计的.Zetalisp比当时发布的Common Lisp大得多,并包含一个名为Flavors的早期对象系统.Zetalisp的大部分内容都是以面向对象的方式实现的 - 这样做的性价比和Lisp Machines的性能影响并不大 - 但他们拥有专门的处理器,它们具有优化的指令集.所以Common Lisp没有包含任何对象系统,因此它在当时的典型处理器上略微优化了性能.在Common Lisp的更高版本中,添加了一个对象系统 - 当有足够的Lisp面向对象扩展体验时 - 请记住,我们谈论的是80年代早期.
这个1984年的Common Lisp有一种有限的通用行为.例如,该函数REMOVE
适用于序列 - 而序列是一种新类型,它具有矢量和列表作为子类型.
后来Common Lisp从1986年开始标准化,其中一个正在为Common Lisp寻找一个对象系统 - 没有一个是足够好的 - 所以基于New Flavors开发了一个新的(来自Symbolics,上面提到的更新版本)味道)和Common Loops(来自Xerox PARC).那些已经具有通用功能,但是单一调度.然后CLOS添加了多个调度.
决定不用CLOS泛型函数替换基本函数 - 一个原因是性能:CLOS泛型函数需要一个相对复杂的调度机制,并且这个调度在运行时决定.没有CLOS功能可以进行静态编译时调度,并且也没有标准化的功能来使类的部分"密封" - >以便它们不能被更改.因此,像CLOS这样的高动态系统具有运行时成本.
一些函数被定义为CLOS泛型函数(如PRINT-OBJECT
),并且一些实现具有大部分Common Lisp和CLOS实现(流,条件......) - 但这是特定于实现的并且不是标准所要求的.还有几个库,它们提供基于CLOS的内置CL功能的功能:例如具有可扩展的基于CLOS的流的I/O.
另请注意,重新定义现有的Common Lisp函数是未定义的行为.
因此,Common Lisp选择提供一个功能强大的对象系统,但是将其留给个别实现,他们希望在基本语言中使用CLOS多少 - 具有标准化为普通非CLOS通用函数的函数的限制通常不应该被用户替换为CLOS功能.
一些Lisp方言/实现尝试处理这些问题,并试图定义一个更快的CLOS变体,然后它将成为大部分语言的基础.例如,参见Apple的Dylan语言.对于一些较新的方法,请参阅Julia语言.
你自己改进的Common Lisp
包( - >符号命名空间)允许您定义自己的改进CL:这里我们定义一个包含所有CL符号的新包,只有cl:remove
自己的符号遮蔽.然后我们定义一个名为CLOS泛型函数bettercl::remove
并编写两个示例方法.
CL-USER 165 > (defpackage "BETTERCL" (:use "CL") (:shadow cl:remove))
#<The BETTERCL package, 1/16 internal, 0/16 external>
CL-USER 166 > (in-package "BETTERCL")
#<The BETTERCL package, 1/16 internal, 0/16 external>
BETTERCL 167 > (defgeneric remove (item whatever))
#<STANDARD-GENERIC-FUNCTION REMOVE 4060000C64>
BETTERCL 168 > (defmethod remove (item (v vector)) (cl:remove item v))
#<STANDARD-METHOD REMOVE NIL (T VECTOR) 40200AB12B>
BETTERCL 169 > (remove 'a #(1 2 3 a b c))
#(1 2 3 B C)
BETTERCL 170 > (defmethod remove ((digit integer) (n integer))
"remove a digit from an integer, returns a new integer"
(let ((s (map 'string
(lambda (item)
(character (princ-to-string item)))
(cl:remove digit
(map 'vector
#'digit-char-p
(princ-to-string n))))))
(if (= (length s) 0) 0 (read-from-string s))))
#<STANDARD-METHOD REMOVE NIL (INTEGER INTEGER) 40200013C3>
BETTERCL 171 > (remove 8 111888111348111)
11111134111
Run Code Online (Sandbox Code Playgroud)
现在您也可以从中导出符号BETTERCL
,这样您就可以在应用程序包中使用此包而不是包CL
.
之前已经使用过这种方法.例如,CLIM(Common Lisp Interface Manager)定义了一个包CLIM-LISP,它用作编程的方言.
Common Lisp有时提供函数和相关的CLOS泛型函数
请参阅标准函数DESCRIBE,它可以通过编写标准CLOS泛型函数DESCRIBE-OBJECT的方法进行扩展.
Common Lisp的改进实验
使标准的Common Lisp的功能扩展的另一种方法,是用的版本,然后使用可扩展的基于CLOS的协议来取代它们.请注意,如何替换标准函数是特定于实现的,并且效果也可能是特定于实现的.例如,如果一个编译器内联内置的功能转换成代码,然后重新定义将会对已经内联的代码没有任何影响.
有关此方法的示例,请参阅Christophe Rhodes 撰写的文章(PDF)Common Lisp中的用户可扩展序列.
何时使用通用功能?
有几点需要注意:
当存在多个相关方法时,定义CLOS泛型函数,这些方法理想地受益于公共扩展机制并且可能被扩展
想想性能的打击
不要对具有类似arglist甚至相同名称的函数使用单个CLOS泛型函数,但这些函数适用于非常不同的域
这意味着您不应该在单个CLOS泛型函数中定义函数,例如:
; do some astrophysics calculations
(defmethod rotate-around ((s star) (u galaxy)) ...)
; do some computation with graphics objects
(defmethod rotate-around (shape (a axis)) ...)
Run Code Online (Sandbox Code Playgroud)
例如写作:before
,:around
和:after
方法可能不会导致有用的结果.
一个可以有两个不同的rotate-around
通用函数,一个在一个包中ASTRO-PHYSICS
,另一个在一个包中GRAPHICS-OBJECTS
.因此,这些方法不会在同一个CLOS泛型函数中,并且扩展它们可能更容易.