Rutas
/
Factor
Factor
/
Ejercicios
/
Cinta del telegrafista
Cinta del telegrafista

Cinta del telegrafista

Ejercicio de aprendizaje

Introducción

Ya has usado streams en channel-chatter y te has deshecho de ellos en boatswains-bilge. Este ejercicio es tu primer stream personalizado: una clase que implementa el protocolo para que el mismo código with-input-stream / read1 / stream-read1 que funcionaba con un <string-reader> funcione con el tuyo.

Mixins: la otra mitad de la jerarquía de clases

Ya conoces TUPLE: child < parent … de dragons-descendants, una jerarquía de un solo padre en la que los slots descienden. Factor tiene una segunda forma de extender el grafo de clases: los mixins abiertos. Un MIXIN: m declara una clase sin slots a la que otras clases pueden unirse a posteriori con INSTANCE: c m. La pertenencia es de muchos a muchos: una clase puede ser instancia de tantos mixins como quiera.

El protocolo de streams usa dos mixins para marcar qué mitad de la interfaz implementa una clase:

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

Un stream puede ser uno, el otro o ambos. Los métodos genéricos de input-stream (o output-stream) despachan entonces a cualquier clase que se haya unido al mixin: así es como el read1, stream-contents, with-input-stream, etc. del entorno encuentran el camino hasta tu clase.

Los mixins son abiertos: puedes añadir un nuevo miembro después de que se declare el mixin, incluso en un vocabulario diferente. Ese es el contraste con TUPLE: < parent, donde la relación queda fijada cuando se declara la clase hija.

Los genéricos del protocolo

Los genéricos del protocolo viven en 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 devuelve el siguiente byte (o f al llegar al final del stream); stream-write1 escribe uno. stream-read-unsafe es la lectura por lotes de más bajo nivel que proporcionan la mayoría de los streams de entrada; stream-read se construye sobre ella y se encarga de la asignación. stream-element-type indica si el stream trabaja con bytes (+byte+) o con caracteres (+character+); cuando envuelves otro stream, la implementación más sencilla simplemente delega en el stream subyacente.

La limpieza viene de destructors

Los streams son desechables: un stream personalizado extiende disposable e implementa dispose*. Ese es el framework de boatswains-bilge, aplicado a una clase de stream:

TUPLE: my-stream < disposable underlying ;

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

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

Una vez que eso está en su sitio, todos los combinadores with-… de channel-chatter funcionan en tu stream automáticamente.

Instrucciones

Eres el telegrafista inalámbrico de un carguero. La señal llega por una línea ruidosa, entremezclada con el Morse que intentas escuchar: la maquinaria del barco, la estática del tiempo y alguna que otra conversación ajena. Para captar el mensaje, haces pasar el cable por un dispositivo de cinta que descarta todo salvo los símbolos Morse válidos (., - y el espacio que separa las letras).

Tu tarea es construir la cinta como un flujo de entrada personalizado de Factor.

1. Define la cinta

Define una tupla tape que extienda disposable y tenga un slot, wire, para el flujo de entrada subyacente.

TUPLE: tape < disposable wire ;

Después, escribe el constructor <tape> ( wire -- tape ). Usa new-disposable (no new) para que el framework de destructores registre la cinta, y guarda el flujo subyacente en el slot wire.

2. Añade el mixin input-stream

Declara que tape es un input-stream:

INSTANCE: tape input-stream

Eso hace que los combinadores predeterminados del protocolo se despachen a tu clase.

3. Lee un símbolo Morse

Implementa M: tape stream-read1 ( tape -- elt/f ). Lee bytes del cable subyacente hasta que te encuentres con uno de CHAR: ., CHAR: - o CHAR: \s (espacio). Devuelve ese byte. Si el cable devuelve f (fin del flujo), devuelve f también: la cinta ha terminado.

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

4. Delega el tipo de elemento

Implementa M: tape stream-element-type ( tape -- type ) delegando en el cable: la cinta no cambia el tipo de elementos que se leen.

5. Limpia

Implementa M: tape dispose* ( tape -- ). El framework llama a tu método exactamente una vez por cada dispose; solo tienes que liberar el cable subyacente.

Con esas cinco piezas en su sitio, la cinta funciona como un flujo de entrada normal: with-input-stream, stream-read y las demás palabras del protocolo se despachan a tus métodos automáticamente.

Editar en GitHub El enlace se abre en una ventana o pestaña nueva
Factor Exercism

¿Listo para empezar Cinta del telegrafista?

Regístrate en Exercism para aprender y dominar Factor con 47 conceptos163 ejercicios y mentoría humana real, todo gratis.