Tracks
/
Factor
Factor
/
Übungen
/
Telegrafenband
Telegrafenband

Telegrafenband

Lernübung

Einführung

Du hast Streams bereits in channel-chatter verwendet und sie in boatswains-bilge wieder freigegeben. Diese Übung ist dein erster eigener Stream: eine Klasse, die das Protokoll implementiert, sodass derselbe Code mit with-input-stream / read1 / stream-read1, der mit einem <string-reader> funktioniert hat, auch mit deinem funktioniert.

Mixins: die andere Hälfte der Klassenhierarchie

Du kennst TUPLE: child < parent … bereits aus dragons-descendants: eine Hierarchie mit einem einzigen Elternteil, in der Slots nach unten weitergegeben werden. Factor hat einen zweiten Weg, den Klassen-Graphen zu erweitern: offene Mixins. Ein MIXIN: m deklariert eine Klasse ohne Slots, der andere Klassen nachträglich mit INSTANCE: c m beitreten können. Die Zugehörigkeit ist m:n: Eine Klasse kann Instanz beliebig vieler Mixins sein.

Das Stream-Protokoll verwendet zwei Mixins, um zu markieren, welche Hälfte der Schnittstelle eine Klasse implementiert:

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

Ein Stream kann das eine, das andere oder beides sein. Generische Methoden auf input-stream (oder output-stream) leiten dann an jede Klasse weiter, die dem Mixin beigetreten ist. So finden die allgemein verfügbaren read1, stream-contents, with-input-stream usw. ihren Weg zu deiner Klasse.

Mixins sind offen: Du kannst noch nach der Deklaration des Mixins ein neues Mitglied hinzufügen, sogar in einem anderen Vokabular. Das ist der Unterschied zu TUPLE: < parent, wo die Beziehung feststeht, sobald die Kindklasse deklariert wird.

Die generischen Methoden des Protokolls

Die generischen Methoden des Protokolls liegen in 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 gibt das nächste Byte zurück (oder f am Ende des Streams); stream-write1 schreibt eines. stream-read-unsafe ist das gebündelte Lesen auf niedrigerer Ebene, das die meisten Eingabe-Streams anbieten; stream-read baut darauf auf und kümmert sich um die Speicherzuweisung. stream-element-type gibt an, ob der Stream mit Bytes (+byte+) oder Zeichen (+character+) arbeitet. Wenn du einen anderen Stream umhüllst, delegiert die einfachste Implementierung einfach an den darunterliegenden.

Das Aufräumen kommt aus destructors

Streams sind Disposables: Ein eigener Stream erweitert disposable und implementiert dispose*. Das ist das Framework aus boatswains-bilge, angewendet auf eine Stream-Klasse:

TUPLE: my-stream < disposable underlying ;

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

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

Sobald das steht, funktionieren alle Kombinatoren with-… aus channel-chatter automatisch mit deinem Stream.

Anleitung

Du bist der Funktelegrafist auf einem Frachter. Das Signal kommt über eine verrauschte Leitung herein: Schiffsmaschinen, Wetterrauschen, gelegentlich fremdes Geplauder, alles durchsetzt mit dem echten Morsecode, auf den du horchst. Um die Nachricht aufzufangen, schickst du den Draht durch ein Bandgerät, das alles außer gültigen Morsesymbolen (., - und dem Leerzeichen, das die Buchstaben trennt) herausfiltert.

Deine Aufgabe ist es, das Band als eigenen Factor-Eingabestream zu bauen.

1. Definiere das Band

Definiere ein tape-Tupel, das disposable erweitert und einen Slot wire für den darunterliegenden Eingabestream enthält.

TUPLE: tape < disposable wire ;

Dann schreib den Konstruktor <tape> ( wire -- tape ). Verwende new-disposable (nicht new), damit das Destruktor-Framework das Band registriert, und speichere den darunterliegenden Stream im Slot wire.

2. Tritt dem input-stream-Mixin bei

Deklariere tape als input-stream:

INSTANCE: tape input-stream

Dadurch leiten die Standard-Kombinatoren des Protokolls an deine Klasse weiter.

3. Lies ein Morsesymbol ein

Implementiere M: tape stream-read1 ( tape -- elt/f ). Lies Bytes vom darunterliegenden Draht, bis du auf CHAR: ., CHAR: - oder CHAR: \s (Leerzeichen) stößt. Gib dieses Byte zurück. Wenn der Draht f zurückgibt (Ende des Streams), gib ebenfalls f zurück, dann ist das Band fertig.

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

4. Delegiere den Elementtyp

Implementiere M: tape stream-element-type ( tape -- type ), indem du an den Draht delegierst. Das Band ändert die Art der gelesenen Elemente nicht.

5. Räum auf

Implementiere M: tape dispose* ( tape -- ). Das Framework ruft deine Methode genau einmal pro dispose auf. Du musst nur den darunterliegenden Draht freigeben.

Wenn diese fünf Teile stehen, funktioniert das Band wie ein normaler Eingabestream: with-input-stream, stream-read und die übrigen Wörter des Protokolls leiten automatisch an deine Methoden weiter.

Über GitHub bearbeiten Der Link öffnet sich in einem neuen Fenster oder Tab
Factor Exercism

Bereit, mit Telegrafenband zu starten?

Melde dich bei Exercism an, um Factor mit 47 Konzepte163 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.