Common Lisp defclass简记

ANSI 的 CLOS(The Common Lisp Object System)中定义 Common Lisp 面向对象的基本规则,与大多数动态语言家族的其他语言类似(诸如 Smalltalk,Python,Ruby,甚至基于原型的JavaScript ),对象方法可以在运行时动态定义。

CLOS

CLOS 是基于类的,Common Lisp 使用 defclass 标准宏来定义类。CLOS 定义的类具有以下特性:

类的数据成员,亦即属性(attributes),标准名称为槽(slot),可通过以下键值参数设置其读写权限:

  1. :accessor:定义该属性可读可写,后跟的值相当于 getter/setter 的方法名;
  2. :reader:定义该属性可读,后跟的值相当于 getter 的方法名;
  3. :writer:定义该属性可写,后跟的值相当于 setter 的方法名;

与Ruby有一些类似,Ruby中通过:attr_accessor:attr_reader:attr_writer实现一样的效果。实际上,Common Lisp 的槽值在设置权限后,实际上只是给出读写函数的别名,并非真正意义上的权限。比如说设置槽foo为 “:writer foo”,实际上通过 slot-value 依然可读取 foo 的值。面向对象的特征:封装、范型、多态和继承,本质上就是一种组织、重用代码的方式,Common Lisp 作为古老的函数式语言,在 CLOS 中并不违背面向对象思想。

广义函数

类方法,与C++,Java 等静态语言不同,这里的类方法“不属于”类,而是定义为一种叫做广义函数(generic function)的东西,广义函数是为了让类方法保持函数式特性的必要做法。因为严格地说,类方法不是函数,因为类方法不是头等公民,即数据类型不是函数,不能当作参数传递,不能被返回。然而,广义函数的数据类型即函数,具有头等公民——函数,的一切特性。

继承

在继承方面,CLOS 支持单继承和多继承,单继承可参考 Practical Common Lisp 一书。是的,Common Lisp 支持多继承,但书中只是简单一带,并未详细介绍。从 C++ 多继承机制并未被广泛应用可看出,多继承带来的复杂性比带来的好处多许多,多数场合用不到。

defclass 示例

下例改编自 Youtube 视频 newthinktank – learn lisp one video 中的例子,参见:http://www.newthinktank.com/2015/07/learn-lisp-one-video/

mammal.lisp

;;; Define a simple class in common lisp
;;; 
;;; :class mammal
;;; :reference `http://www.newthinktank.com/2015/07/learn-lisp-one-video/`
;;; :reference `Practical Common Lisp`

;; :initarg defines the key used to assign to the slot
;; :initform defines a default value 
;; :accessor generates getter and setters for the slot using the name you provide. 
;; :reader generates only a getter
;; :writer generate only a setter")
(defclass mammal ()
  ((name
     :initarg :name
     :initform (error "Must provide a name"))
   (sound
     :initarg :sound
     :initform "No Sound"
     :accessor mammal-sound)))

;; Define a method: make-sound
(defgeneric make-sound (mammal))

(defmethod make-sound ((the-mammal mammal))
  (format t "~a says ~a ~%"
          (slot-value the-mammal 'name)
          (slot-value the-mammal 'sound)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Define setter for :name
(defgeneric (setf mammal-name) (value the-mammal))

(defmethod (setf mammal-name) (value (the-mammal mammal))
  (setf (slot-value the-mammal 'name) value))

;; Define getter for :name
(defgeneric mammal-name (the-mammal))

(defmethod mammal-name ((the-mammal mammal))
  (slot-value the-mammal 'name))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Basic single inheritance
;; class dog inherits from class mammal
(defclass dog (mammal)
  ())

;; Define a special initialization method for class dog
(defmethod initialize-instance :after ((a-dog dog) &key)
  (setf (slot-value a-dog 'sound) "Woof"))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Simple test
(defparameter *rover*
  (make-instance 'dog :name "Rover"))

(mammal-name *rover*)
; -> "Rover"

(mammal-sound *rover*)
; -> "Woof"

(setf (mammal-name *rover*) "Roverx")

(mammal-name *rover*)
; -> "Roverx"

(make-sound *rover*)
; -> Roverx says Woof
; -> NIL

小结

Common Lisp 作为最古老的函数式语言,其面向对象的特性在后来才加入,即使 class 是 Common Lisp 的后来者,然而它仍然早于 Python 等语言。现如今,CLOS 已被很多语言借鉴吸收,重新了解 CLOS 将有助于我们更好地理解当代流行编程语言的设计思想。

参考

  1. http://www.newthinktank.com/2015/07/learn-lisp-one-video/
  2. Practical Common Lisp

作者: YanWen

Web 开发者

发表评论

Fill in your details below or click an icon to log in:

WordPress.com 徽标

You are commenting using your WordPress.com account. Log Out /  更改 )

Google photo

You are commenting using your Google account. Log Out /  更改 )

Twitter picture

You are commenting using your Twitter account. Log Out /  更改 )

Facebook photo

You are commenting using your Facebook account. Log Out /  更改 )

Connecting to %s