Phasers: BEGIN, UNITCHECK, CHECK, INIT, END#

Πέντε ειδικά ονομασμένα μπλοκ επιτρέπουν στον κώδικα να συνδεθεί με τις μεταβάσεις που κάνει ένα πρόγραμμα ανάμεσα στο να μεταγλωττίζεται και στο να εκτελείται. Μοιάζουν με υπορουτίνες αλλά δεν είναι: μπορείτε να γράψετε πολλά από το καθένα, κανένα δεν έχει καλέσιμο όνομα, και το καθένα πυροδοτείται αυτόματα στη δική του στιγμή.

BEGIN     { ... }   # during compilation, as soon as defined
UNITCHECK { ... }   # right after this compilation unit is compiled
CHECK     { ... }   # after the whole program is compiled
INIT      { ... }   # just before the runtime begins
END       { ... }   # just before the interpreter exits

Το πρόθεμα sub (sub BEGIN { ... }) γίνεται αποδεκτό αλλά δεν προσθέτει τίποτα και αποθαρρύνεται. Αυτά είναι μπλοκ, όχι ονομασμένες υπορουτίνες· δεν μπορείτε να τα καλέσετε, μόνο να τα αφήσετε να πυροδοτηθούν.

Πότε εκτελείται το καθένα#

Ένα πρόγραμμα Perl ζει σε δύο φάσεις: μια φάση μεταγλώττισης που αναλύει τον πηγαίο κώδικα σε μια εσωτερική μορφή, και μια φάση εκτέλεσης που τον εκτελεί. Οι phasers σημαδεύουν το όριο.

  • Το BEGIN εκτελείται τη στιγμή που αναλύεται η αγκύλη κλεισίματός του, προτού καν διαβαστεί το υπόλοιπο του αρχείου. Έτσι λειτουργεί η use: ένα use είναι μια require τυλιγμένη σε BEGIN συν import, οπότε τα εισαγόμενα ονόματα υπάρχουν για τον κώδικα που ακολουθεί. Μόλις εκτελεστεί ένα BEGIN, γίνεται απροσδιόριστο και η μνήμη του ανακτάται.

  • Το UNITCHECK εκτελείται μόλις η μονάδα μεταγλώττισης που το περιέχει ολοκληρώσει τη μεταγλώττισή της. Μια μονάδα είναι το κύριο πρόγραμμα, ένα άρθρωμα που φορτώνεται με require, ένα string eval, ένα do FILE, ή κώδικας που μεταγλωττίζεται από την κατασκευή regex (?{ }). Επειδή οι μονάδες ολοκληρώνουν τη μεταγλώττισή τους σε διαφορετικές στιγμές, το UNITCHECK είναι ο μόνος phaser του οποίου ο χρονισμός παρακολουθεί το μεμονωμένο αρχείο και όχι το πρόγραμμα ως σύνολο.

  • Το CHECK εκτελείται μία φορά, αφού τελειώσει η αρχική φάση μεταγλώττισης ολόκληρου του προγράμματος και πριν ξεκινήσει η φάση εκτέλεσης.

  • Το INIT εκτελείται μία φορά, λίγο πριν αρχίσει η φάση εκτέλεσης, μετά από όλα τα μπλοκ CHECK.

  • Το END εκτελείται μία φορά, όσο πιο αργά γίνεται: αφού έχει τελειώσει το πρόγραμμα (συμπεριλαμβανομένου του μετά από μια die), λίγο πριν εξέλθει ο διερμηνευτής.

Τα BEGIN και UNITCHECK είναι δεμένα με την ανάλυση, όχι με τη φάση του διερμηνευτή, οπότε μπορούν να πυροδοτηθούν κατά τη διάρκεια οποιασδήποτε φάσης: ένα string eval κατά τον χρόνο εκτέλεσης εξακολουθεί να μεταγλωττίζει το σώμα του, και κάθε BEGIN ή UNITCHECK μέσα του εκτελείται εκεί και τότε. Τα CHECK και INIT, αντιθέτως, σημαδεύουν το εφάπαξ όριο μεταγλώττισης/εκτέλεσης του κύριου προγράμματος.

Πολλαπλότητα και σειρά εκτέλεσης#

Μπορείτε να γράψετε οποιονδήποτε αριθμό από το κάθε μπλοκ. Όλα εκτελούνται, με καθορισμένη σειρά:

  • BEGIN: first in, first out (FIFO) όπως συναντώνται κατά τη μεταγλώττιση. Το πρώτο BEGIN στη σειρά του πηγαίου κώδικα εκτελείται πρώτο.

  • UNITCHECK: last in, first out (LIFO) μέσα σε κάθε μονάδα, αφού μεταγλωττιστεί αυτή η μονάδα.

  • CHECK: LIFO, μετά από όλη τη μεταγλώττιση.

  • INIT: FIFO, λίγο πριν τον χρόνο εκτέλεσης.

  • END: LIFO κατά την έξοδο. Το τελευταίο END που ορίζεται είναι το πρώτο που εκτελείται.

Η διδακτική περίπτωση είναι ένα αρχείο που αναμειγνύει συνηθισμένες εντολές με τα και τα πέντε είδη μπλοκ. Τα μπλοκ δεν εκτελούνται εκεί όπου είναι γραμμένα· εκτελούνται στη φάση τους, με τη σειρά τους ανά φάση, ενώ οι συνηθισμένες εντολές εκτελούνται ενδιάμεσα κατά τον χρόνο εκτέλεσης. Εννοιολογικά, για ένα μεμονωμένο αρχείο:

1. BEGIN blocks      -- FIFO, during compilation
2. UNITCHECK blocks  -- LIFO, after this unit compiles
3. CHECK blocks      -- LIFO, after all compilation
4. INIT blocks       -- FIFO, just before runtime
5. ordinary code     -- run phase, in source order
6. END blocks        -- LIFO, at exit

Έτσι τα BEGIN και UNITCHECK εκτελούνται πριν από οποιοδήποτε CHECK· το CHECK εκτελείται πριν το INIT· το INIT εκτελείται πριν την πρώτη γραμμή συνηθισμένου κώδικα· και κάθε END εκτελείται μετά την τελευταία. Μέσα στα BEGIN και INIT, τα νωρίτερα γραμμένα μπλοκ εκτελούνται νωρίτερα· μέσα στα UNITCHECK, CHECK και END, τα αργότερα γραμμένα μπλοκ εκτελούνται νωρίτερα.

Τα μπλοκ END αναλυτικά#

Το END είναι ο phaser καθαρισμού. Εκτελείται με LIFO ώστε η αποδόμηση να ξετυλίγεται με την αντίστροφη σειρά της εγκατάστασης, όπως το περιμένουν οι εμφωλευμένοι πόροι.

Μέσα σε ένα μπλοκ END, το $? κρατά την τιμή που το πρόγραμμα πρόκειται να περάσει στην exit. Μπορείτε να το διαβάσετε για να μάθετε πώς τελειώνει το πρόγραμμα, και μπορείτε να του αναθέσετε τιμή για να αλλάξετε τον κωδικό εξόδου:

END {
    $? = 0 if $? == 42;     # rewrite one exit code on the way out
}

Προσέξτε μην αλλοιώσετε το $? κατά λάθος. Η εκτέλεση οτιδήποτε μέσω της system μέσα σε ένα μπλοκ END αντικαθιστά το $? με την κατάσταση του παιδιού, που στη συνέχεια γίνεται ο κωδικός εξόδου του προγράμματος εκτός αν το αποθηκεύσετε και το επαναφέρετε.

Τα μπλοκ END δεν εκτελούνται σε αρκετές περιπτώσεις:

  • Υπό τον διακόπτη -c (έλεγχος σύνταξης μόνο μεταγλώττισης). Ο κύριος κώδικας δεν εκτελείται, ούτε και τα μπλοκ END.

  • Όταν η μεταγλώττιση αποτυγχάνει. Ένα πρόγραμμα που δεν μεταγλωττίζεται δεν φτάνει ποτέ στο σημείο όπου θα πυροδοτούνταν τα μπλοκ END.

  • Όταν η διεργασία αντικαθιστά τον εαυτό της με exec. Η εικόνα έχει χαθεί· δεν απομένει τίποτα για να εκτελέσει το μπλοκ.

  • Όταν η διεργασία τερματίζεται από ένα μοιραίο σήμα που δεν παγιδεύετε. Πιάστε το σήμα μόνοι σας (μέσω %SIG) αν χρειάζεται να εκτελεστεί ο καθαρισμός.

Ένα μπλοκ END που δημιουργείται μέσα σε ένα string eval δεν εκτελείται όταν τελειώνει αυτό το eval. Καταχωρείται όπως κάθε άλλο μπλοκ END του πακέτου του και εκτελείται με σειρά LIFO λίγο πριν εξέλθει ο διερμηνευτής.

Το ${^GLOBAL_PHASE} και οι φάσεις#

Η μεταβλητή μόνο για ανάγνωση ${^GLOBAL_PHASE} ονομάζει την τρέχουσα φάση του διερμηνευτή. Οι τιμές της επιτρέπουν σε κώδικα που εκτελείται σε περισσότερες από μία φάσεις να βρει πού βρίσκεται. Κατά τη διάρκεια ενός μπλοκ CHECK διαβάζεται CHECK· κατά το INIT, INIT· κατά τον συνηθισμένο κώδικα χρόνου εκτέλεσης, RUN· κατά τη διάρκεια ενός μπλοκ END, END. Δείτε το ${^GLOBAL_PHASE} για την πλήρη λίστα τιμών και τη χρονογραμμή των φάσεων.

Μπλοκ defer#

Ένα μπλοκ defer είναι ο μικρής κλίμακας ξάδελφος του END. Εκτελείται όταν εξέρχεται η περικλείουσα εμβέλεια μπλοκ, για οποιονδήποτε λόγο, αντί όταν εξέρχεται ολόκληρο το πρόγραμμα. Καταφύγετε στο defer για να ζευγαρώσετε την απόκτηση με την αποδέσμευση μέσα σε μια υπορουτίνα ή βρόχο· καταφύγετε στο END για αποδόμηση σε επίπεδο διεργασίας που πρέπει να γίνει μία φορά, στο τέλος τέλος.

Δείτε επίσης#

  • BEGIN και use - το use είναι μια φόρτωση-και-εισαγωγή τυλιγμένη σε BEGIN· ο πιο συνηθισμένος λόγος για να ενδιαφέρεστε για τον χρονισμό της φάσης μεταγλώττισης.

  • ${^GLOBAL_PHASE} - διαβάστε την τρέχουσα φάση από μέσα σε οποιοδήποτε μπλοκ για να μάθετε πού βρίσκεστε.

  • defer - καθαρισμός με εμβέλεια μπλοκ, το σωστό εργαλείο όταν η εμβέλεια του END σε όλο το πρόγραμμα είναι υπερβολικά ευρεία.

  • $? - η κατάσταση εξόδου που ένα μπλοκ END μπορεί να διαβάσει και να ξαναγράψει.

  • %SIG - παγιδεύστε τα σήματα που διαφορετικά θα παρέκαμπταν εντελώς τα μπλοκ END.

  • exit - αυτό λίγο πριν το οποίο εκτελούνται τα μπλοκ END· η τιμή που φέρει είναι το $? που βλέπει ένα μπλοκ END.