Κάποιοι βρόχοι δεν μπορούν να γραφτούν ως "κάνε αυτό για κάθε στοιχείο μιας ακολουθίας": πρέπει να συνεχίζεις μέχρι να αλλάξει κάποια συνθήκη. Ο συνδυαστής while της Factor είναι το εργαλείο για αυτό: μια παράθεση κατηγορήματος λέει "συνέχισε;", μια παράθεση σώματος κάνει τη δουλειά ενός βήματος, και το while επαναλαμβάνει μέχρι η παράθεση κατηγορήματος να δώσει f.
while και until
while ( pred body -- )
until ( pred body -- )
Οι pred και body είναι και οι δύο παραθέσεις που λειτουργούν στη στοίβα δεδομένων. Το while επαναλαμβάνει όσο η pred αφήνει μια τιμή που αξιολογείται ως αληθής· το until κάνει το αντίθετο: επαναλαμβάνει μέχρι η pred να γίνει αληθής (δηλαδή, όσο η pred αφήνει f).
Μια μόνο τιμή κατάστασης μπορεί να κυλήσει στη στοίβα ανάμεσα στο κατηγόρημα και το σώμα:
USING: kernel math ;
5 [ dup 0 > ] [ dup . 1 - ] while drop
! prints 5 4 3 2 1
Ιχνηλάτηση: το κατηγόρημα dup 0 > ρίχνει μια ματιά στον μετρητή που τρέχει· το σώμα dup . 1 - τον εκτυπώνει και τον μειώνει κατά ένα· ο βρόχος σταματά μόλις ο μετρητής φτάσει στο 0. Το drop στο τέλος πετάει αυτό το τελευταίο 0 από τη στοίβα.
while*: κράτησε την τιμή του κατηγορήματοςwhile* ( pred body -- )
Το while νοιάζεται μόνο αν το κατηγόρημά του άφησε μια αληθή τιμή, και πετάει την ίδια την τιμή πριν τρέξει το σώμα. Το while* αντίθετα δίνει αυτή την τιμή στο σώμα. Καταφεύγεις σε αυτό όποτε ο έλεγχος και το σώμα θέλουν το ίδιο φρεσκοϋπολογισμένο πράγμα: μια αναζήτηση, ένα ταίριασμα, ένα token που διαβάστηκε.
Εδώ κάθε όνομα δείχνει στο επόμενο μέσα σε μια αλυσίδα. Το κατηγόρημα αναζητά το τρέχον όνομα· όσο υπάρχει επόμενο όνομα, το while* το περνά στο σώμα, το οποίο το εκτυπώνει και συνεχίζει από εκεί:
USING: assocs kernel locals prettyprint ;
:: print-chain ( start links -- )
start [ links at ] [ dup . ] while* ;
"a" H{ { "a" "b" } { "b" "c" } } print-chain
! prints "b" then "c"
Το links at επιστρέφει το επόμενο όνομα, ή f όταν ένα όνομα δεν έχει καταχώριση. Όσο επιστρέφει κάποιο όνομα, το while* δίνει αυτό το όνομα στο dup ., το οποίο το εκτυπώνει και το αφήνει ως κλειδί για την επόμενη αναζήτηση. Το πρώτο f σταματά τον βρόχο: το while* το πετάει, οπότε δεν περισσεύει τίποτα.
Όταν ο βρόχος κουβαλά περισσότερες από μία τιμές, ας πούμε μια λίστα που απομένει και ένα τρέχον άθροισμα, το πάνω-κάτω στη στοίβα γίνεται κουραστικό. Το πιο καθαρό μοτίβο είναι να δηλώσεις τοπικές μεταβλητές με το :::
:: word ( inputs -- outputs ) ... ;
Μέσα στο σώμα, το :> name! προσθέτει μια τοπική μεταβλητή που μπορεί να τροποποιηθεί· οι αναγνώσεις χρησιμοποιούν το name, οι εγγραφές το name! (το οποίο καταναλώνει τη νέα τιμή από τη στοίβα και την αναθέτει).
USING: kernel locals math sequences ;
! Walk a sequence from the front, summing as we go
:: sum-by-hand ( seq -- total )
seq :> rest!
0 :> running!
[ rest empty? ] [
rest unclip [ rest! ] dip
running + running!
] until
running ;
Δύο τοπικές μεταβλητές εδώ (οι rest και running) καλύπτουν όλη την κατάσταση του βρόχου: το κατηγόρημα τις εξετάζει, το σώμα τις ενημερώνει, και η τελική τιμή επιστρέφεται στο τέλος.
Το unclip ( seq -- rest first ) αφαιρεί το πρώτο στοιχείο μιας ακολουθίας, κάτι βολικό όταν ο βρόχος θέλει να καταναλώσει μια λίστα ένα στοιχείο σε κάθε επανάληψη. Η μορφή [ rest! ] dip ενημερώνει το rest με την ουρά που απομένει, ενώ αφήνει το στοιχείο που μόλις αφαιρέθηκε πάνω στη στοίβα για να το χρησιμοποιήσει το σώμα.
Η φίλη σου η Li Mei έχει ένα μπαρ με χυμούς, όπου πουλάει νόστιμους χυμούς από ανάμεικτα φρούτα. Είσαι τακτικός πελάτης στο μαγαζί της και κατάλαβες ότι θα μπορούσες να της κάνεις τη ζωή λίγο πιο εύκολη με λίγο κώδικα Factor.
Στη Li Mei αρέσει να λέει στους πελάτες της από πριν πόση ώρα θα περιμένουν για έναν χυμό από το μενού. Της είναι δύσκολο να θυμάται τους ακριβείς αριθμούς, γιατί ο χρόνος που χρειάζεται για να φτιαχτεί ο κάθε χυμός ποικίλλει:
"Pure Strawberry Joy" θέλει 0.5 λεπτά."Energizer" και ο "Green Garden" θέλουν 1.5 λεπτά ο καθένας."Tropical Island" θέλει 3 λεπτά."All or Nothing" θέλει 5 λεπτά.2.5 λεπτά.Όρισε τη time-to-mix-juice ώστε να παίρνει ένα όνομα χυμού από τη στοίβα και να επιστρέφει τον αριθμό των λεπτών που χρειάζονται για να φτιαχτεί αυτό το ποτό.
"Tropical Island" time-to-mix-juice .
! => 3
"Berries & Lime" time-to-mix-juice .
! => 2.5
Πολλές από τις δημιουργίες της Li Mei περιλαμβάνουν φέτες λάιμ, είτε ως συστατικό είτε ως μέρος της διακόσμησης. Έτσι, όταν ξεκινάει τη βάρδιά της το πρωί, πρέπει να βεβαιωθεί ότι το δοχείο με τις φέτες λάιμ είναι γεμάτο για την ημέρα που έρχεται.
Όρισε τη wedges-from-lime ώστε να παίρνει ένα μέγεθος λάιμ και να επιστρέφει τον αριθμό των φετών που βγάζει: 6 από ένα "small" λάιμ, 8 από ένα "medium", 10 από ένα "large".
Έπειτα, όρισε τη limes-to-cut ώστε να παίρνει τον αριθμό των needed φετών και έναν πίνακα με limes (μεγέθη λάιμ με τη σειρά κοψίματος). Κόβει τα λάιμ ένα ένα, με τη σειρά, και σταματάει μόλις είτε μαζέψει αρκετές φέτες είτε τελειώσουν τα λάιμ. Επίστρεψε τον αριθμό των λάιμ που χρειάστηκε να κόψει.
25 { "small" "small" "large" "medium" "small" } limes-to-cut .
! => 4
Στη Li Mei αρέσει να παρακολουθεί πόση ώρα θα πάρει να φτιαχτούν οι παραγγελίες που περιμένουν οι πελάτες.
Όρισε τη order-times ώστε να παίρνει έναν πίνακα με παραγγελίες και να επιστρέφει έναν πίνακα με τον χρόνο για να φτιαχτεί κάθε παραγγελία, με την ίδια σειρά.
{ "Energizer" "Tropical Island" } order-times .
! => { 1.5 3 }
Η Li Mei δουλεύει πάντα μέχρι τις 3 μ.μ. Τότε αναλαμβάνει ο υπάλληλός της, ο Dmitry. Συχνά υπάρχουν ποτά που έχουν παραγγελθεί αλλά δεν έχουν ετοιμαστεί ακόμη όταν τελειώνει η βάρδια της Li Mei. Τότε ο Dmitry ετοιμάζει τους υπόλοιπους χυμούς.
Για να γίνει πιο εύκολη η παράδοση της βάρδιας, όρισε τη remaining-orders ώστε να παίρνει τον αριθμό των λεπτών time-left που απομένουν στη βάρδια της Li Mei και έναν πίνακα με orders (χυμούς που έχουν παραγγελθεί αλλά δεν έχουν ετοιμαστεί ακόμη). Επίστρεψε τις παραγγελίες που θα χρειαστεί να αναλάβει ο Dmitry, δηλαδή αυτές που δεν προλαβαίνει να ξεκινήσει η Li Mei πριν τελειώσει η βάρδιά της.
Το time-left είναι πάντα μεγαλύτερο από 0. Οι παραγγελίες ετοιμάζονται με τη σειρά που εμφανίζονται στον πίνακα. Μόλις η Li Mei ξεκινήσει έναν χυμό, τον τελειώνει πάντα, ακόμη κι αν χρειαστεί να δουλέψει λίγο παραπάνω. Αν προλαβαίνει να ξεκινήσει κάθε παραγγελία, επίστρεψε έναν κενό πίνακα.
5 { "Energizer" "All or Nothing" "Green Garden" } remaining-orders .
! => { "Green Garden" }
Γράψου στο Exercism για να μάθεις και να κατακτήσεις Factor με 47 έννοιες163 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.