6

泛型似乎提供了一个很好的工具,可以提取一个常用词,让它根据你传递的类型作用于事物,事后具有可扩展性。

但是那些已经被使用但没有被定义为通用的常用词呢?如果我尝试定义 REMOVE,例如:

(defclass reticulator () (splines))

(defmethod remove ((item reticulator)
     sequence &key from-end test test-not start end count key))

我在 SBCL 中收到错误:

COMMON-LISP:REMOVE 已经命名了普通函数或宏。

是否有一种习惯用法或认可的方式来“泛化”这些内置函数之一?人们会这样做吗?

只是为了看看会发生什么,我尝试用泛型覆盖 REMOVE:

(defgeneric remove (item sequence &key from-end test test-not start end count key))

警告:重新定义 COMMON-LISP:REMOVE 在 DEFGENERIC

我不知道是否有一种“好”的方法可以做到这一点,这将通过允许重载给想要赋予单词新含义的特定类型的旧实现。

4

1 回答 1

15

为什么事情是这样的?

Common Lisp 的第一个版本是从 1981/82 年开始设计的,结果被出版为Common Lisp the Language一书1984 年。语言本身很大程度上是基于 Lisp Machine Lisp(又名 Zetalisp)设计的。Zetalisp 比当时出版的 Common Lisp 大得多,并且包括一个称为 Flavors 的早期对象系统。Zetalisp 中的大部分东西都是以面向对象的方式实现的——这确实具有成本效益,而在 Lisp 机器上,性能影响并没有那么大——但它们有专门的处理器,这些处理器具有优化的指令集。因此,Common Lisp 不包含任何对象系统,因此它针对当时典型处理器的性能进行了轻微优化。在更高版本的 Common Lisp 中,将添加一个对象系统——当对 Lisp 的面向对象扩展有足够的经验时——记住,我们谈论的是 80 年代初期。

这个 1984 年的 Common Lisp 具有有限形式的泛型行为。例如,该函数REMOVE适用于序列 - 而序列是一种新类型,它具有向量和列表作为子类型。

后来 Common Lisp 于 1986 年开始标准化,人们正在寻找 Common Lisp 的对象系统 - 没有一个提议足够好 - 因此基于 New Flavors 开发了一个新的系统(来自 Symbolics,上面提到的更新版本Flavours)和 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

现在您还可以从 导出符号BETTERCL,这样您就可以在应用程序包中使用此包,而不是包CL

这种方法以前已经使用过。例如 CLIM(Common Lisp Interface Manager)定义了一个包CLIM-LISP,它被用作编程的方言。

Common Lisp 有时同时提供一个函数和一个相关的 CLOS 泛型函数

请参阅标准函数DESCRIBE,可以通过为标准 CLOS 通用函数DESCRIBE-OBJECT编写方法来扩展它。

Common Lisp 的改进实验

使标准 Common Lisp 函数可扩展的另一种方法是将它们替换为使用可扩展的基于 CLOS 协议的版本。请注意,如何替换标准函数是特定于实现的,效果也可能是特定于实现的。例如,如果编译器已将内置函数内联到代码中,则重新定义将不会对已内联的代码产生影响。

有关此方法的示例,请参阅 Christophe Rhodes 的论文 (PDF) User-extensible Sequences in Common Lisp

什么时候使用泛型函数?

有几件事情要记住:

  • 当有多个相关方法时定义 CLOS 泛型函数,理想情况下受益于通用扩展机制并且可能会被扩展

  • 考虑性能影响

  • 不要将单个 CLOS 泛型函数用于具有相似 arglist 甚至名称相同但适用于非常不同的域的函数

这意味着您不应在单个 CLOS 泛型函数中定义函数,例如:

; do some astrophysics calculations
(defmethod rotate-around ((s star) (u galaxy)) ...)

; do some computation with graphics objects
(defmethod rotate-around (shape (a axis)) ...)

例如写作:before:around:after方法可能不会导致有用的结果。

一个可以有两个不同的rotate-around通用函数,一个在 packageASTRO-PHYSICS中,另一个在 package 中GRAPHICS-OBJECTS。因此,这些方法将不在同一个 CLOS 通用函数中,扩展它们可能会更容易。

于 2018-12-01T21:28:40.687 回答