<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                第十二章 對象和類 =================== 類是描述了一組有共同行為的對象。由類描述的對象稱為類的一個實例。類指定了其實例擁有的`屬性`(原文為slot卡槽)的名稱,而這些`屬性`的值由實例自身來進行填充。類同樣也指定了可以應用于其實例的`方法`(method)。屬性值可以是任何形式,但方法的值必須是過程。 類具有繼承性。因此,一個類可以是另一個類的子類,我們稱另一個類為它的父類。一個子類不僅有它自己“直接的”屬性和方法,也會繼承它的父類的所有屬性和方法。如果一個類里有與其父類相同名稱的屬性和方法,那么僅保留子類的屬性和方法。 ## 12.1 一個簡單的對象系統 現在我們用Scheme來實現一個基本的對象系統。對于每個類,我們只允許有一個父類(單繼承性)。如果我們不想指定一個父類,我們可以用`#t`作為一個“元”父類,既沒有屬性,也沒有方法。而`#t`的父類則認為是它自己。 作為一次嘗試,用結構`standard-class`來定義類應該是很好的一種方式,用結構的字段來保存屬性名字,父類以及方法。前兩個字段我們分別叫做`slots`和`superclass`。我們將使用兩個字段來描述方法,用`method-names`字段來描述類的方法的名稱列表,用`method-vector`字段來保存一個矢量,里面放著類的方法。這是`standard-class`的定義: ```scheme (defstruct standard-class slots superclass method-names method-vector) ``` 我們可以用`make-standard-class`,即`standard-class`的制造程序(見第九章)來創建一個新的類: ```scheme (define trivial-bike-class (make-standard-class 'superclass #t 'slots '(frame parts size) 'method-names '() 'method-vector #())) ``` 這是一個非常簡單的類,更加復雜的類會有有意義的父類和方法,這需要在創建類時進行大量的初始化設置,我們希望把這些工作隱藏在創建類的過程中。因此我們定義一個`create-class`宏來對`make-standard-class`進行適當的調用。 ```scheme (define-macro create-class (lambda (superclass slots . methods) `(create-class-proc ,superclass (list ,@(map (lambda (slot) `',slot) slots)) (list ,@(map (lambda (method) `',(car method)) methods)) (vector ,@(map (lambda (method) `,(cadr method)) methods))))) ``` 我們稍后再介紹`create-class-proc`程序的定義。 `make-instance`程序創建類的一個實例,由類中包含的信息產生一個新的向量。實例向量的格式非常簡單:它的第一個元素指向這個類(引用),余下的元素都是屬性值。`make-instance`的第一個參數是一個類,后面的參數是成對的序列,而每一個“對”是屬性名稱和該實例中屬性的值。 ```scheme (define make-instance (lambda (class . slot-value-twosomes) ;Find `n', the number of slots in `class'. ;Create an instance vector of length `n + 1', ;because we need one extra element in the instance ;to contain the class. (let* ((slotlist (standard-class.slots class)) (n (length slotlist)) (instance (make-vector (+ n 1)))) (vector-set! instance 0 class) ;Fill each of the slots in the instance ;with the value as specified in the call to ;`make-instance'. (let loop ((slot-value-twosomes slot-value-twosomes)) (if (null? slot-value-twosomes) instance (let ((k (list-position (car slot-value-twosomes) slotlist))) (vector-set! instance (+ k 1) (cadr slot-value-twosomes)) (loop (cddr slot-value-twosomes)))))))) ``` 這是一個類的實例化的例子: ```scheme (define my-bike (make-instance trivial-bike-class 'frame 'cromoly 'size '18.5 'parts 'alivio)) ``` 這將`my-bike`變量綁定到如下所示的實例上。 ```scheme #(<trivial-bike-class> cromoly 18.5 alivio) ``` `<trivial?bike?class>`是一個Scheme數據(另一個向量)代表之前定義的`trivia-bike-class`的值。 `class-of`程序返回該實例對應的類: ```scheme (define class-of (lambda (instance) (vector-ref instance 0))) ``` 這里假定`class-of`的參數是一個類的實例,即一個向量,其第一個元素指向`standard-class`的一些實例。我們可能想使`class-of`對我們給定的任何類型Scheme對象返回一個合適的值。 ```scheme (define class-of (lambda (x) (if (vector? x) (let ((n (vector-length x))) (if (>= n 1) (let ((c (vector-ref x 0))) (if (standard-class? c) c #t)) #t)) #t))) ``` 不是用`standard-class`創建的Scheme對象的類被認為是`#t`,即“元類”。 `slot-value`過程和`set!slot-value`過程用來訪問和改變一個類實例的值: ```scheme (define slot-value (lambda (instance slot) (let* ((class (class-of instance)) (slot-index (list-position slot (standard-class.slots class)))) (vector-ref instance (+ slot-index 1))))) (define set!slot-value (lambda (instance slot new-val) (let* ((class (class-of instance)) (slot-index (list-position slot (standard-class.slots class)))) (vector-set! instance (+ slot-index 1) new-val)))) ``` 我們現在來解決`create-class-proc`的定義問題。這個過程接受一個父類,一個屬性的列表,一個方法名稱的列表和一個包含方法體的向量,并適當調用`make-standard-class`程序。唯一困難的部分是給定的屬性字段的值。由于一個類必須包括它的父類的屬性,因此不能只有`create-class`提供的屬性參數。我們必須把所給的屬性追加到父類的屬性中,并保證沒有重復的屬性。 ```scheme (define create-class-proc (lambda (superclass slots method-names method-vector) (make-standard-class 'superclass superclass 'slots (let ((superclass-slots (if (not (eqv? superclass #t)) (standard-class.slots superclass) '()))) (if (null? superclass-slots) slots (delete-duplicates (append slots superclass-slots)))) 'method-names method-names 'method-vector method-vector))) ``` 過程`delete-duplicates`接受一個列表`s`為參數,返回一個新列表,該列表只包含`s`中每個元素的最后一次出現。 ```scheme (define delete-duplicates (lambda (s) (if (null? s) s (let ((a (car s)) (d (cdr s))) (if (memv a d) (delete-duplicates d) (cons a (delete-duplicates d))))))) ``` 現在談談方法的應用。我們通過使用`send`程序調用一個類實例的方法。`send`的參數是方法的名字,緊接著是類實例,以及除了類實例本身之外的該方法的其他參數。由于方法儲存在實例的類中而不是在實例本身中,因此`send`會在該實例對于的類中尋找該方法。如果沒有找到,則到父類中尋找,如此直到找完整個繼承鏈: ```scheme (define send (lambda (method instance . args) (let ((proc (let loop ((class (class-of instance))) (if (eqv? class #t) (error 'send) (let ((k (list-position method (standard-class.method-names class)))) (if k (vector-ref (standard-class.method-vector class) k) (loop (standard-class.superclass class)))))))) (apply proc instance args)))) ``` 我們現在可以定義一些更有趣的類了: ```scheme (define bike-class (create-class #t (frame size parts chain tires) (check-fit (lambda (me inseam) (let ((bike-size (slot-value me 'size)) (ideal-size (* inseam 3/5))) (let ((diff (- bike-size ideal-size))) (cond ((<= -1 diff 1) 'perfect-fit) ((<= -2 diff 2) 'fits-well) ((< diff -2) 'too-small) ((> diff 2) 'too-big)))))))) ``` 這里,`bike-class`包括一個名為`check-fit`的方法,它接受一個自行車的實例和一個褲腿的尺寸作為參數,并報告該車對這種褲腿尺寸的人的適應性。 我們再來定義`my-bike`: ```scheme (define my-bike (make-instance bike-class 'frame 'titanium ; I wish 'size 21 'parts 'ultegra 'chain 'sachs 'tires 'continental)) ``` 檢查這個車與褲腿尺寸為32的某個人是否搭配: ```scheme (send 'check-fit my-bike 32) ``` 我們再定義子類`bike-class`。 ```scheme (define mtn-bike-class (create-class bike-class (suspension) (check-fit (lambda (me inseam) (let ((bike-size (slot-value me 'size)) (ideal-size (- (* inseam 3/5) 2))) (let ((diff (- bike-size ideal-size))) (cond ((<= -2 diff 2) 'perfect-fit) ((<= -4 diff 4) 'fits-well) ((< diff -4) 'too-small) ((> diff 4) 'too-big)))))))) ``` `Mtn-bike-class`添加了一個名為`suspension`的屬性。并定義了一個稍微不同的名為`check-fit`的方法。 ## 12.2 類也是實例 到這里為止,精明的讀者可能已經發現了:類本身可以是某些其他類(如“元類”)的實例。注意所有類都有一些相同的特點:每個都有屬性、父類、方法名稱的列表和包含方法體的向量。`make-instance`看起來像是他們所共享的方法。這意味著我們可以通過另一個類(當然也是某個類的實例啦)來指定這些共同的特點。 具體的說就是我們可以重寫我們的類實現并實現其自身(好別扭)。使用面向對象的方法,這樣我們可以確保不會遇到雞生蛋,蛋生雞的問題。這樣我們會跳出`class`結構和它相關的過程并余下的方法來把類定義為對象。 我們現在把`standard-class`作為其他類的父類。特別的,`standard-class`必須是它自己的一個實例。那么`standard-class`應該是什么樣子的呢? 我們知道`standard-class`是一個實例,而且我們用一個向量來表示這個實例。所以最終是一個向量,其第一個元素是它的父類,也就是它自己,而余下的元素是屬性值。我們已經確定有四個所有類都必須有的屬性,因此`standard-class`是一個5個元素的向量。 ```scheme (define standard-class (vector 'value-of-standard-class-goes-here (list 'slots 'superclass 'method-names 'method-vector) #t '(make-instance) (vector make-instance))) ``` 注意到`standard-class`這個向量并沒有被完全填充:符號`value?of?standard?class?goes?here`此時僅僅做占位用。現在我們已經定義了一個`standard-class`的值,現在我們可以用它來確定它自己的類,即它本身。 ```scheme (vector-set! standard-class 0 standard-class) ``` 注意我們不能用`class`結構提供的過程了。我們必須把下面的形式: ```scheme (standard-class? x) (standard-class.slots c) (standard-class.superclass c) (standard-class.method-names c) (standard-class.method-vector c) (make-standard-class ...) ``` 換成: ```scheme (and (vector? x) (eqv? (vector-ref x 0) standard-class)) (vector-ref c 1) (vector-ref c 2) (vector-ref c 3) (vector-ref c 4) (send 'make-instance standard-class ...) ``` ## 12.3 多重繼承 我們可以容易的修改這個對象系統使類可以有一個以上的父類。我們重新定義`standard?class`來添加一個屬性叫`class?precedence?list`取代`superclass`,一個類的`class?precedence?list`是它所有父類的列表,而不只有通過`create-class`創建時指定的“直接”的父類。從這個名字可以看出其超類是以一種特定的順序來存放的,前面的超類有比后面超類更高的優先級。 ```scheme (define standard-class (vector 'value-of-standard-class-goes-here (list 'slots 'class-precedence-list 'method-names 'method-vector) '() '(make-instance) (vector make-instance))) ``` 不僅屬性列表改變來存放新的屬性,而且`superclass`屬性也從`#t`變為`()`,這是因為`standard?class`的`class?precedence?list`必須是一個列表。我們可以令它的值為`(#t)`,但是我們不會提到元類,由于它在每個類的`class?precedence?list`中。 宏`create-class`也需要修改來接受一個超類的列表而不是一個單獨的超類。 ```scheme (define-macro create-class (lambda (direct-superclasses slots . methods) `(create-class-proc (list ,@(map (lambda (su) `,su) direct-superclasses)) (list ,@(map (lambda (slot) `',slot) slots)) (list ,@(map (lambda (method) `',(car method)) methods)) (vector ,@(map (lambda (method) `,(cadr method)) methods)) ))) ``` `create?class?proc`必須根據提供的超類給出類的優先級列表,并根據優先級給出屬性列表: ```scheme (define create-class-proc (lambda (direct-superclasses slots method-names method-vector) (let ((class-precedence-list (delete-duplicates (append-map (lambda (c) (vector-ref c 2)) direct-superclasses)))) (send 'make-instance standard-class 'class-precedence-list class-precedence-list 'slots (delete-duplicates (append slots (append-map (lambda (c) (vector-ref c 1)) class-precedence-list))) 'method-names method-names 'method-vector method-vector)))) ``` 過程`append-map`是一個`append`和`map`的組合: ```scheme (define append-map (lambda (f s) (let loop ((s s)) (if (null? s) '() (append (f (car s)) (loop (cdr s))))))) ``` 過程`send`在尋找一個方法時必須從左到右搜索類的優先級列表: ```scheme (define send (lambda (method-name instance . args) (let ((proc (let ((class (class-of instance))) (if (eqv? class #t) (error 'send) (let loop ((class class) (superclasses (vector-ref class 2))) (let ((k (list-position method-name (vector-ref class 3)))) (cond (k (vector-ref (vector-ref class 4) k)) ((null? superclasses) (error 'send)) (else (loop (car superclasses) (cdr superclasses)))) )))))) (apply proc instance args)))) ``` -------------------------- 理論上我們可以把方法也定義為屬性(值為一個過程),但是有很多理由不這樣做,類的實例共享方法但是通常有不同的屬性值。也就是說,方法可以包括在類定義中,而且不需要每次實例化時都進行設置——就像屬性那樣。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看