Έχεις ήδη χρησιμοποιήσει ροές στο channel-chatter και τις έχεις αποδεσμεύσει στο boatswains-bilge. Αυτή η άσκηση είναι η πρώτη δική σου προσαρμοσμένη ροή: μια κλάση που υλοποιεί το πρωτόκολλο, ώστε ο ίδιος κώδικας with-input-stream / read1 / stream-read1 που δούλευε σε ένα <string-reader> να δουλεύει και στη δική σου.
Ξέρεις ήδη το TUPLE: child < parent … από το dragons-descendants: μια ιεραρχία με έναν μόνο γονέα, όπου τα πεδία περνούν προς τα κάτω. Η Factor έχει και έναν δεύτερο τρόπο να επεκτείνεις το γράφημα κλάσεων: τα ανοιχτά mixins. Ένα MIXIN: m δηλώνει μια κλάση χωρίς πεδία, στην οποία άλλες κλάσεις μπορούν να ενταχθούν εκ των υστέρων με INSTANCE: c m. Η συμμετοχή είναι πολλά-προς-πολλά: μια κλάση μπορεί να είναι στιγμιότυπο όσων mixins θέλει.
Το πρωτόκολλο των ροών χρησιμοποιεί δύο mixins για να δηλώσει ποιο μισό της διεπαφής υλοποιεί μια κλάση:
INSTANCE: my-stream input-stream ! readable
INSTANCE: my-stream output-stream ! writable
Μια ροή μπορεί να είναι το ένα, το άλλο ή και τα δύο. Οι γενικές μέθοδοι στο input-stream (ή στο output-stream) απευθύνονται, λοιπόν, σε οποιαδήποτε κλάση έχει ενταχθεί στο mixin: έτσι βρίσκουν τον δρόμο τους προς την κλάση σου τα read1, stream-contents, with-input-stream κ.λπ.
Τα mixins είναι ανοιχτά: μπορείς να προσθέσεις ένα νέο μέλος αφού δηλωθεί το 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 επιστρέφει το επόμενο byte (ή f στο τέλος της ροής). Το stream-write1 γράφει ένα. Το stream-read-unsafe είναι η μαζική ανάγνωση χαμηλότερου επιπέδου που παρέχουν οι περισσότερες ροές εισόδου· το stream-read χτίζεται πάνω της και αναλαμβάνει τη δέσμευση μνήμης. Το stream-element-type λέει αν η ροή δουλεύει με bytes (+byte+) ή με χαρακτήρες (+character+). Όταν τυλίγεις μια άλλη ροή, η απλούστερη υλοποίηση αναθέτει απλώς στην υποκείμενη ροή.
destructors
Οι ροές είναι disposables: μια προσαρμοσμένη ροή επεκτείνει το disposable και υλοποιεί το dispose*. Αυτό είναι το framework από το 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 ). Διάβασε bytes
από το υποκείμενο καλώδιο μέχρι να συναντήσεις ένα από τα CHAR: .,
CHAR: - ή CHAR: \s (κενό). Επίστρεψε αυτό το byte. Αν το καλώδιο
επιστρέψει 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. Απλώς κάνε dispose στο
υποκείμενο καλώδιο.
Με αυτά τα πέντε κομμάτια στη θέση τους, η ταινία λειτουργεί ως
κανονική ροή εισόδου. Τα with-input-stream, stream-read και οι
άλλες λέξεις του πρωτοκόλλου δρομολογούνται αυτόματα στις μεθόδους σου.
Γράψου στο Exercism για να μάθεις και να κατακτήσεις Factor με 47 έννοιες163 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.