Scheme 编程/面向对象
Scheme 有许多面向对象系统。本章将介绍不同的对象系统。
R7RS 中的一个对象库是 Virgo。要导入它,请输入以下内容
> (import (virgo user))
许多实现,例如 Chibi、Gauche、Guile 和 Chicken,都有自己的 CLOS 类系统,它们在语法上可能大不相同,但在语义上非常接近。Virgo 被选择用于可移植性,但这些概念将适用于所有实现。
这是定义类的格式
> (define-class <point> ()
(x 'init-value 0.0)
(y 'init-value 0.0))
只需使用 make 创建一个新对象,并初始化其值。
> (define pt (make <point>))
用于获取和设置值的程序分别是 slot-ref 和 slot-set!。
> (slot-ref pt 'x)
0.0
> (slot-set! pt 'x 100.0)
> (slot-ref pt 'x)
100.0
与 Java 或 C# 不同,Virgo 有一个 CLOS 类对象系统,这意味着方法不属于单个类。相反,我们定义一个泛型,然后为该泛型分配方法以处理不同的类。这是一个例子
> (define-generic distance)
> (define-method distance ((p <point>))
(sqrt (+ (square (slot-ref p 'x)) (square (slot-ref p 'y)))))
> (distance pt)
100.0
方法也可以与多个类一起使用。为了举一个无稽之谈的例子
> (define-generic append-anything)
> (define-method append-anything ((p <point>) (s <string>))
(string-append (number->string (slot-ref p 'x)) s))
> (append-anything pt "Hello")
"100.0Hello"
Virgo 还具有继承功能。这是一个例子
> (define-class <3point> (<point>)
(z 'init-value 0.0))
> (define pt3 (make <3point>))
> (slot-ref pt3 'x)
0.0
> (slot-ref pt3 'z)
0.0
与 CLOS 类系统不同,Prometheus 使用基于原型的对象而不是类。此外,方法与 Java 或 C# 中类似,绑定到对象。此外,对象不是不交集类型,而是将传递给它的第一个参数解释为方法名称的程序。
如果您使用的是 R7RS 实现并且已安装 Prometheus 库,则可以使用以下命令加载库
> (import (prometheus user))
定义对象与 CLOS 类系统没有太大区别。请注意,对象必须从另一个对象继承,除非它是根对象。如果您不希望它从其他任何东西继承,则该对象应该从 *the-root-object* 继承。以我们的点示例为例
> (define-object <point> (*the-root-object*)
(x set-x! 0.0)
(y set-y! 0.0))
define-object 语法只是语法糖,您并不总是需要使用 define-object 创建新实例。相反,您可以像这样克隆一个对象,回想一下对象只是程序
> (define pt (<point> 'clone))
同样,获取和设置只是向对象传递不同的符号。
> (pt 'x)
0.0
> (pt 'set-x! 100.0)
> (pt 'x)
100.0
当然,对象与它们相关联的方法。方法是至少有两个参数的闭包:self,传递给闭包的对象,以及 resend,它调用父对象的行為。对此的语法糖是 define-method
> (define-method (<point> 'distance self resend)
(sqrt (+ (square (self 'x)) (square (self 'y)))))
> (pt 'distance)
100.0
也可以在使用 define-object 语法糖定义对象时为对象定义方法。
> (define-object <3point> (<point>)
(z set-z! 0.0)
((distance self resend)
(sqrt (+ (square (self 'x))
(square (self 'y))
(square (self 'z))))))
> (define pt3 (<3point> 'clone))
> (pt3 'set-x! 3.0)
> (pt3 'set-z! 4.0)
> (pt3 'distance)
5.0
"YASOS" 或 "Yet Another Scheme Object System" 是 Scheme 的一个特别简单的对象系统。YASOS 与 T(Scheme 的一种旧方言)的对象系统非常相似。让我们来看看它的特性。
如果您使用的是 R7RS 实现并且已安装 YASOS 库,则可以使用以下命令加载库
> (import (yasos))
如果您已安装并加载了 SLIB,您也可以执行此操作
> (require 'yasos)
与 CLOS 类系统相比,YASOS 可能感觉有点反常。首先,我们声明操作和谓词,然后我们创建对象。让我们继续使用点示例进行比较
> (define-predicate point?)
> (define-operation (get-x p))
> (define-operation (get-y p))
> (define-operation (set-x! p value))
> (define-operation (set-y! p value))
> (define-operation (distance p))
现在我们已经定义了点的操作,我们将定义一个对象,该对象在其方法中处理这些操作。对象的 method 语法与 Prometheus 类似。我们不会使用内置的构造函数语法,而是会定义一个返回新构造对象的程序。
> (define (make-point x y)
(object
((point? self) #t)
((get-x self) x)
((get-y self) y)
((set-x! self value) (set! x value))
((set-y! self value) (set! y value))
((distance self)
(sqrt (+ (square x) (square y))))))
> (define pt (make-point 0.0 0.0))
> (get-x pt)
0.0
> (set-x! pt 100.0)
> (get-x pt)
100.0
> (set-y! pt 100.0)
> (distance pt)
141.4213562373095
这种设计还意味着必须在构造对象时定义 method,并且不能在之后添加。
YASOS 使用 object-with-ancestors 语法来允许继承,这将赋予对象“祖先”或“父”对象的特征。
> (define-predicate point3?)
> (define-operation (get-z p))
> (define-operation (set-z! p value))
> (define (make-point3 x y z)
(object-with-ancestors ((p (make-point x y)))
((point3? self) #t)
((get-z self) z)
((set-z! self value) (set! z value))
((distance self)
(sqrt (+ (square x) (square y) (square z))))))
> (define pt3 (make-point3 1.0 2.0 3.0))
> (get-x pt3)
1.0
> (get-z pt3)
3.0
> (distance pt3)
3.7416573867739413