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