Το παιχνίδι με τη στοίβα είναι μια χαρά για σύντομες λέξεις. Όταν μια λέξη έχει πολλαπλές εισόδους και χρειάζεται να τις αναφέρεις με το όνομά τους, ο Factor προσφέρει τις τοπικές μεταβλητές, δεσμεύσεις με όνομα που εισάγονται με :: (για ορισμούς λέξεων) ή [| | ] (για ενσωματωμένες λάμδα).
:: ( inputs -- outputs )Αντικατέστησε το : με :: και τα ονόματα των εισόδων γίνονται τοπικές μεταβλητές στο σώμα:
USING: locals ;
:: hypotenuse ( a b -- c )
a a *
b b *
+ sqrt ;
Σύγκρινε με την έκδοση που ανακατεύει τη στοίβα:
! DOCTEST: SKIP (shown for comparison — would re-define hypotenuse)
: hypotenuse ( a b -- c )
[ sq ] bi@ + sqrt ;
Και οι δύο είναι έγκυρες, αλλά οι τοπικές μεταβλητές λάμπουν όταν μια είσοδος χρησιμοποιείται πάνω από μία φορά ή όταν η σειρά των πράξεων δεν ευθυγραμμίζεται φυσικά με τη σειρά της στοίβας.
:>
Μέσα σε ένα σώμα :: (ή σε μια ενσωματωμένη λάμδα [| ... |]), το :> βγάζει μια τιμή από τη στοίβα δεδομένων και τη δεσμεύει σε μια νέα τοπική μεταβλητή:
value :> name ! immutable binding
value :> name! ! mutable binding (name! is its setter)
Η αμετάβλητη μορφή είναι η συνηθισμένη. Χρησιμοποίησέ την για να ονομάσεις ένα ενδιάμεσο αποτέλεσμα, ώστε το σώμα να διαβάζεται από πάνω προς τα κάτω:
USING: locals math ;
:: average-3 ( x y z -- avg )
x y + z + :> sum
sum 3 / ;
Η παραλλαγή με ! δηλώνει ότι η τοπική μεταβλητή μπορεί να τροποποιηθεί· αργότερα στο σώμα, το value name! (χωρίς άνω και κάτω τελεία, χωρίς :>) της αναθέτει νέα τιμή:
USING: kernel locals math math.order ;
:: clamped ( x lo hi -- y )
x :> v!
v lo < [ lo v! ] when
v hi > [ hi v! ] when
v ;
Οι τοπικές μεταβλητές που μπορούν να τροποποιηθούν λάμπουν μέσα στις λέξεις επανάληψης που θα συναντήσεις σε επόμενες ασκήσεις (π.χ. while).
[let: μια εμβέλεια από οπουδήποτεΤο :> λειτουργεί μόνο μέσα σε μια λεκτική εμβέλεια, την οποία δημιουργούν αυτόματα τα :: και [| ... |]. Για να εισάγεις τοπικές μεταβλητές από μια απλή λέξη : ή από το listener, τύλιξε τον κώδικα σε [let ... ]:
[let code :> name code :> name ... body ]
Κάθε :> name δεσμεύει την τιμή που βρίσκεται στην κορυφή της στοίβας από την προηγούμενη έκφραση· οι δεσμεύσεις είναι ορατές στο υπόλοιπο της φόρμας.
USING: locals math ;
[let 3 :> x 4 :> y x x * y y * + ] . ! => 25
Το [let είναι η γέφυρα ανάμεσα στους ορισμούς : στυλ στοίβας και τις δεσμεύσεις με όνομα, και είναι βολικό όταν μόνο ένα κομμάτι μιας λέξης ωφελείται από τις τοπικές μεταβλητές.
[| inputs | body ]
Μέσα σε μια παράθεση, το [| inputs | body ] εισάγει τοπικές μεταβλητές στις οποίες το σώμα μπορεί να αναφέρεται με το όνομά τους. Αυτό είναι ιδιαίτερα χρήσιμο με λέξεις υψηλότερης τάξης όπως το assoc-map, του οποίου η παράθεση δέχεται το key και το value:
USING: assocs locals ;
H{ { "a" 1 } { "b" 2 } }
[| key value | key value neg ] assoc-map .
! => H{ { "a" -1 } { "b" -2 } }
Το ίδιο σχήμα λειτουργεί και για το map-index (στη βιβλιοθήκη [sequences][sequences]), του οποίου η παράθεση δέχεται το στοιχείο και τη θέση του. Τα ονόματα κάνουν σαφές ποιος τελεστέος παίζει ποιον ρόλο:
USING: locals sequences ;
! Divide each element by its 1-based position:
{ 10 80 270 } [| elt i | elt i 1 + / ] map-index .
! => { 10 40 90 }
Η επίδραση στη στοίβα της λάμδα καθορίζεται από τη λίστα εισόδων και από αυτά που παράγει το σώμα της.
Κατέφυγε στις τοπικές μεταβλητές όταν:
dup/swap/rot που θα το διορθώσουν είναι δυσανάγνωστα.assoc-map).Όταν το σώμα είναι μια καθαρή γραμμική ροή, οι απλοί ορισμοί : παραμένουν πιο σύντομοι και τουλάχιστον εξίσου ευανάγνωστοι.
assoc-map: μία καταχώριση τη φοράΤο assoc-map (στη βιβλιοθήκη assocs) αντιστοιχίζει μια παράθεση σε κάθε καταχώριση μιας συσχετιστικής συλλογής:
assoc-map ( assoc quot: ( key value -- newkey newvalue ) -- newassoc )
Μια λάμδα με τοπικές μεταβλητές είναι ο πιο καθαρός τρόπος να γράψεις την παράθεση.
cond
Όταν πρέπει να αποφασίσεις με βάση το f ή μια αριθμητική τιμή, το cond έχει ακριβώς το σωστό σχήμα, και δεν χρειάζεται case, επειδή δεν υπάρχει μία και μοναδική τιμή για σύγκριση.
Είσαι πάλι στην κουζίνα και βελτιώνεις τη συνταγή για λαζάνια από το Leah's Luscious Lasagna. Οι εργασίες εδώ σου ζητούν να χειριστείς πολλές εισόδους ταυτόχρονα, και τότε ακριβώς είναι που τα locals, οι ονομασμένες δεσμεύσεις της Factor, δικαιώνουν την ύπαρξή τους.
Όρισε τη cooking-status ώστε να παίρνει μια ένδειξη του χρονοδιακόπτη
από τη στοίβα και να επιστρέφει μια συμβολοσειρά κατάστασης.
0 → "Lasagna is done."
f (ο χρονοδιακόπτης δεν είχε οριστεί ποτέ) → "You forgot to set the timer."
"Not done, please wait."
12 cooking-status . ! => "Not done, please wait."
0 cooking-status . ! => "Lasagna is done."
f cooking-status . ! => "You forgot to set the timer."
Όρισε τη preparation-time ώστε να παίρνει έναν πίνακα με ονόματα
στρώσεων και έναν μέσο αριθμό λεπτών ανά στρώση, και να επιστρέφει τον
συνολικό χρόνο προετοιμασίας.
{ "sauce" "noodles" "sauce" "meat" "mozzarella" "noodles" } 3 preparation-time .
! => 18
Όρισε τη quantities ώστε να παίρνει έναν πίνακα με ονόματα στρώσεων και
να επιστρέφει δύο τιμές: τα γραμμάρια ζυμαρικών που χρειάζονται (50 g
ανά στρώση ζυμαρικών) και τα λίτρα σάλτσας που χρειάζονται (1/5 λίτρου
ανά στρώση σάλτσας).
{ "sauce" "noodles" "sauce" "meat" "mozzarella" "noodles" } quantities .s
! => 100
! => 2/5
Ο φίλος σου στέλνει μια λίστα με συστατικά· το τελευταίο στοιχείο είναι
το μυστικό του. Όρισε τη add-secret-ingredient ώστε να παίρνει τη δική
του λίστα και τη δική σου και να επιστρέφει τη λίστα σου με το μυστικό
στο τέλος.
{ "noodles" "sauce" "mozzarella" "kampot pepper" }
{ "noodles" "meat" "sauce" "mozzarella" }
add-secret-ingredient .
! => { "noodles" "meat" "sauce" "mozzarella" "kampot pepper" }
Η συνταγή του βιβλίου μαγειρικής βγάζει δύο μερίδες. Όρισε τη
scale-recipe ώστε να παίρνει μια συνταγή (έναν hashtable από όνομα
συστατικού σε ποσότητα) και έναν αριθμό μερίδων-στόχο, και να επιστρέφει
μια νέα συνταγή κλιμακωμένη ανάλογα.
H{ { "noodles" 200 } { "sauce" 1/2 } { "mozzarella" 1 } { "meat" 100 } }
4 scale-recipe .
! => H{ { "noodles" 400 } { "sauce" 1 } { "mozzarella" 2 } { "meat" 200 } }
Γράψου στο Exercism για να μάθεις και να κατακτήσεις Factor με 47 έννοιες163 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.