Ми вже використовували потоки в channel-chatter і позбувалися їх у boatswains-bilge. Ця вправа - наш перший власний потік. Це клас, який реалізує протокол, тож той самий код із with-input-stream / read1 / stream-read1, який працював із <string-reader>, працюватиме і з нашим.
Ми вже знаємо TUPLE: child < parent … з dragons-descendants: ієрархію з одним батьком, де слоти передаються вниз. У Factor є другий спосіб розширити граф класів: відкриті міксини. MIXIN: m оголошує клас без слотів, до якого інші класи можуть долучитися згодом за допомогою INSTANCE: c m. Належність тут багато-до-багатьох: клас може бути екземпляром стількох міксинів, скількох захоче.
Протокол потоків використовує два міксини, щоб позначити, яку половину інтерфейсу реалізує клас:
INSTANCE: my-stream input-stream ! readable
INSTANCE: my-stream output-stream ! writable
Потік може бути одним, іншим або обома. Узагальнені методи на input-stream (або output-stream) тоді спрямовуються до будь-якого класу, що долучився до міксина. Саме так read1, stream-contents, with-input-stream тощо знаходять дорогу до нашого класу.
Міксини відкриті: ми можемо додати нового члена вже після оголошення міксина, навіть в іншому словнику. Це і є контраст із 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 і реалізує dispose*. Це той самий механізм із boatswains-bilge, застосований до класу потоку:
TUPLE: my-stream < disposable underlying ;
: <my-stream> ( underlying -- s )
my-stream new-disposable swap >>underlying ;
M: my-stream dispose* underlying>> dispose ;
Щойно це готово, всі комбінатори with-… із channel-chatter автоматично працюють із нашим потоком.
Ми — телеграфісти бездротового звʼязку на вантажному судні. Сигнал надходить зашумленою лінією (корабельні механізми, атмосферні завади, уривки чужої мови), переплітаючись із тим кодом Морзе, який ми шукаємо. Щоб упіймати повідомлення, ми пропускаємо дріт крізь пристрій-стрічку, який відкидає все, крім дійсних символів коду Морзе (., - і пробіл, що розділяє літери).
Наше завдання — збудувати стрічку як власний вхідний потік Factor.
Визначте кортеж tape, який успадковує disposable і містить один слот wire для базового вхідного потоку.
TUPLE: tape < disposable wire ;
Потім напишіть конструктор <tape> ( wire -- tape ). Використайте new-disposable (а не new), щоб framework деструкторів зареєстрував стрічку, і збережіть базовий потік у слоті wire.
Оголосіть tape як input-stream:
INSTANCE: tape input-stream
Завдяки цьому типові комбінатори протоколу звертаються до нашого класу.
Реалізуйте 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
Реалізуйте M: tape stream-element-type ( tape -- type ), делегувавши роботу дроту: стрічка не змінює тип елементів, які читаються.
Реалізуйте M: tape dispose* ( tape -- ). Framework викликає наш метод рівно один раз на кожен dispose; просто звільніть базовий дріт.
Коли ці пʼять частин на місці, стрічка працює як звичайний вхідний потік: with-input-stream, stream-read та інші слова протоколу автоматично звертаються до наших методів.
Зареєструйтеся на Exercism, щоб вивчати й опановувати Factor, а також 47 концепцій163 вправи та справжнє наставництво від людей, і все це безкоштовно.