Track
/
Factor
Factor
/
Esercizi
/
Nastro del telegrafista
Nastro del telegrafista

Nastro del telegrafista

Esercizio di apprendimento

Introduzione

Hai già usato gli stream in channel-chatter e li hai smaltiti in boatswains-bilge. Questo esercizio è il primo stream personalizzato che scriverai: una classe che implementa il protocollo, così lo stesso codice with-input-stream / read1 / stream-read1 che funzionava su un <string-reader> funziona anche su quello che scrivi.

I mixin: l'altra metà della gerarchia delle classi

Conosci già TUPLE: child < parent … da dragons-descendants: una gerarchia a genitore singolo in cui gli slot scendono verso il basso. Factor ha un secondo modo per estendere il grafo delle classi: i mixin aperti. Un MIXIN: m dichiara una classe senza slot a cui altre classi possono unirsi in un secondo momento con INSTANCE: c m. L'appartenenza è molti-a-molti: una classe può essere istanza di quanti mixin vuole.

Il protocollo degli stream usa due mixin per indicare quale metà dell'interfaccia implementa una classe:

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

Uno stream può essere l'uno, l'altro o entrambi. I metodi generici su input-stream (o output-stream) vanno poi in dispatch verso qualunque classe che abbia aderito al mixin: è così che le funzioni ambientali come read1, stream-contents, with-input-stream e così via trovano la strada verso la classe che scrivi.

I mixin sono aperti: puoi aggiungere un nuovo membro dopo che il mixin è stato dichiarato, anche in un vocabolario diverso. È qui il contrasto con TUPLE: < parent, dove la relazione è fissata nel momento in cui si dichiara la classe figlia.

I generici del protocollo

I generici del protocollo vivono 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 restituisce il byte successivo (o f alla fine dello stream); stream-write1 ne scrive uno. stream-read-unsafe è la lettura in blocco di livello più basso che la maggior parte degli stream di input fornisce; stream-read è costruita sopra di essa e gestisce l'allocazione. stream-element-type dice se lo stream tratta byte (+byte+) o caratteri (+character+); quando avvolgi un altro stream, l'implementazione più semplice delega semplicemente a quello sottostante.

La pulizia arriva da destructors

Gli stream sono disposable: uno stream personalizzato estende disposable e implementa dispose*. È il framework di boatswains-bilge, applicato a una classe di stream:

TUPLE: my-stream < disposable underlying ;

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

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

Una volta che tutto questo è a posto, tutti i combinatori with-… di channel-chatter funzionano automaticamente sul tuo stream.

Istruzioni

Sei il telegrafista senza fili su un mercantile. Il segnale arriva su una linea disturbata: rumori dei macchinari della nave, disturbi atmosferici, ogni tanto chiacchiere in lingue straniere, frammisto al Morse che stai cercando di ascoltare. Per catturare il messaggio fai passare il filo attraverso un dispositivo a tape che elimina tutto tranne i simboli Morse validi (., - e lo spazio che separa le lettere).

Il tuo compito è costruire la tape come stream di input personalizzato in Factor.

1. Definire la tape

Definisci una tupla tape che estende disposable e contiene un solo slot, wire, per lo stream di input sottostante.

TUPLE: tape < disposable wire ;

Poi scrivi il costruttore <tape> ( wire -- tape ). Usa new-disposable (non new) così il framework dei distruttori registra la tape, e memorizza lo stream sottostante nello slot wire.

2. Aderire al mixin input-stream

Dichiara che tape è un input-stream:

INSTANCE: tape input-stream

In questo modo i combinatori predefiniti del protocollo vengono indirizzati alla tua classe.

3. Leggere un simbolo Morse

Implementa M: tape stream-read1 ( tape -- elt/f ). Leggi i byte dal wire sottostante finché non ne incontri uno tra CHAR: ., CHAR: - o CHAR: \s (spazio). Restituisci quel byte. Se il wire restituisce f (fine dello stream), restituisci f anche tu: la tape ha finito.

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

4. Delegare il tipo di elemento

Implementa M: tape stream-element-type ( tape -- type ) delegando al wire: la tape non cambia il tipo di elementi che vengono letti.

5. Fare pulizia

Implementa M: tape dispose* ( tape -- ). Il framework chiama il tuo metodo esattamente una volta per ogni dispose: basta chiamare dispose sul wire sottostante.

Con questi cinque pezzi a posto, la tape funziona come un normale stream di input: with-input-stream, stream-read e tutte le altre parole del protocollo vengono indirizzate automaticamente ai tuoi metodi.

Modifica tramite GitHub Il link si apre in una nuova finestra o scheda
Factor Exercism

Vuoi iniziare Nastro del telegrafista?

Iscriviti a Exercism per imparare e padroneggiare Factor con 47 concetti163 esercizi e il mentoring di persone reali, tutto gratis.