學習軌道
/
Factor
Factor
/
練習
/
電報員的紙帶
電報員的紙帶

電報員的紙帶

學習練習

簡介

你已經在channel-chatter裡用過串流,也在boatswains-bilge裡將它們處置掉了。這個練習是你的第一個自訂串流,一個實作了協定的類別,讓那些原本能在<string-reader>上運作的with-input-stream/read1/stream-read1程式碼,也能在你的串流上運作。

Mixin:類別階層的另一半

你已經從dragons-descendants認識了TUPLE: child < parent …,那是一種單一父類別的階層,槽位會往下繼承。Factor 有第二種擴充類別圖的方式:開放式 mixin。MIXIN: m會宣告一個沒有槽位的類別,其他類別之後可以用INSTANCE: c m加入。成員關係是多對多的:一個類別可以同時是任意多個 mixin 的實例。

串流協定用兩個 mixin 來標示一個類別實作了介面的哪一半:

INSTANCE: my-stream input-stream    ! readable
INSTANCE: my-stream output-stream   ! writable

一個串流可以是其中一個、另一個,或兩者兼具。input-stream(或output-stream)上的泛型方法接著會分派到任何加入該 mixin 的類別,這就是環境中的read1、stream-contents、with-input-stream等等找到你的類別的方式。

Mixin 是開放式的:你可以在 mixin 宣告之後才加入新成員,甚至可以在不同的詞彙表中加入。這和TUPLE: < parent形成對比,後者的關係在子類別宣告時就固定了。

協定泛型

協定泛型位於io裡:

stream-read1         ( stream -- elt/f )
stream-read          ( n stream -- seq/f )
stream-read-unsafe   ( n buf stream -- count )
stream-write1        ( elt stream -- )
stream-write         ( seq stream -- )
stream-flush         ( stream -- )
stream-element-type  ( stream -- type )

stream-read1會回傳下一個位元組,串流結束時則回傳f;stream-write1則寫入一個位元組。stream-read-unsafe是大多數輸入串流提供的較低階批次讀取;stream-read建立在其之上,並負責處理配置。stream-element-type會說明這個串流處理的是位元組(+byte+)還是字元(+character+);當你包裝另一個串流時,最簡單的實作就是直接委派給底層串流。

清理機制來自destructors

串流是 disposable:自訂串流會繼承disposable並實作dispose*。那就是boatswains-bilge裡的框架,套用到串流類別上:

TUPLE: my-stream < disposable underlying ;

: <my-stream> ( underlying -- s )
    my-stream new-disposable swap >>underlying ;

M: my-stream dispose* underlying>> dispose ;

一旦這些就緒,channel-chatter裡所有with-…的組合子都會自動作用在你的串流上。

說明

你是貨輪上的無線電報員。訊號從一條充滿雜訊的線路傳來:船上的機械聲、天氣造成的靜電干擾,偶爾還有外語的雜談,全都和你真正想聽的摩斯碼交錯在一起。為了聽清楚訊息,你把線路接上一台磁帶裝置,它會濾掉其他所有東西,只留下有效的摩斯符號(.、-,以及分隔字母的空格)。

你的任務是把這條磁帶打造成 Factor 的自訂輸入串流。

1. 定義磁帶

定義一個tape元組,繼承disposable,並帶有一個wire槽位,用來存放底層的輸入串流。

TUPLE: tape < disposable wire ;

接著撰寫建構子<tape> ( wire -- tape )。請使用new-disposable(而不是new),讓解構子框架能註冊這條磁帶,並把底層串流存進wire槽位。

2. 加入 input-stream mixin

宣告tape是一個input-stream:

INSTANCE: tape input-stream

這樣一來,協定的預設組合子就會分派到你的類別。

3. 讀取一個摩斯符號

實作M: tape stream-read1 ( tape -- elt/f )。從底層的線路讀取位元組,直到遇到CHAR: .、CHAR: -或CHAR: \s(空格)其中之一。回傳那個位元組。如果線路回傳f(串流結束),也回傳f。這條磁帶就到此為止。

"x.y -ab cz" <string-reader> <tape>
[ stream-read1 ] [ stream-read1 ] [ stream-read1 ] tri
! => CHAR: . CHAR: - CHAR: \s

4. 委派元素型別

實作M: tape stream-element-type ( tape -- type ),把工作委派給線路:磁帶不會改變所讀取元素的種類。

5. 清理

實作M: tape dispose* ( tape -- )。框架對每一次dispose只會呼叫你的方法一次;你只要把底層的線路處置掉就好。

這 5 個部分就定位之後,磁帶就能像一般的輸入串流一樣運作:with-input-stream、stream-read,以及其他協定詞彙都會自動分派到你的方法。

透過 GitHub 編輯 連結會在新視窗或分頁中開啟
Factor Exercism

準備好開始 電報員的紙帶 了嗎?

註冊 Exercism,透過 47 個概念163 個練習 和真人引導來學習並精通 Factor,全部免費。