创建通用函数的价值是什么?
我喜欢明确声明通用函数,因为可以添加与通用函数相关的文档和声明(优化速度/空间/调试),以及其他细节,如方法组合(即当您有多个适用于给定调用的方法时,定义哪些方法以及以何种顺序执行)。例如,在这里我可以将talk
定义为具有progn
方法组合(所有方法都像封装在progn
表单中一样执行):
(defgeneric talk (subject)
(:documentation "Say something to standard output")
(:method-combination progn))
这是一个有点牵强的例子,但让我们开始吧:
(defclass human () ())
(defmethod talk progn ((a human))
(print "hello"))
(defclass wolf () ())
(defmethod talk progn ((a wolf))
(print "owooooo!"))
(defclass werewolf (human wolf) ())
定义一个同时继承两个类的类意味着对该类实例的talk
调用可以执行两个方法(按一定的拓扑顺序排序,称为方法解析顺序)。因此,使用此方法组合将执行所有方法:
* (talk (make-instance 'werewolf))
"hello"
"owooooo!"
但是,我认为能够记录通用函数本身就足以使用defgeneric
声明。
通用函数有什么用处?
如果您将talk
定义为通用函数,则允许任何类参与调用talk
的代码(例如库),这是一种在不关闭可能值集合的情况下允许扩展的方法,不像在函数中使用typecase
,您只能列出预定义的一组案例。
例如,标准函数print-object
在您的Lisp实现中的各个时间被调用(在检查器、REPL、调试器中),如果您想要,可以为自定义类型实现一个方法,而无需破解环境的内部。
它们是否像其他OO语言中提供结构的实例?
通用函数与其他OO语言不同,它们不与单个类或实例绑定。它们可以针对多个参数进行专业化1,这意味着没有一个“拥有”通用函数。
1. specialized的定义为如下:
specialize v.t.(一种通用函数)为了为通用函数定义一个方法,或者换句话说,通过为特定类或参数集合赋予其特定含义,以改善通用函数的行为。
背后的理念是方法可以更或少具体:一种具有两个参数a
和b
的方法,它专门针对(a number)
和(b string)
是比只专门针对(b vector)
的方法更具体,而且方法在组合时实际上是从最具体到最不具体的排序方式(相应地从最不具体到最具体)。您甚至可以将一个函数专门针对(a (eql 10))
,以仅覆盖参数a
等于eql
10的特定情况。