do#
Εκτελεί ένα μπλοκ κώδικα ή τρέχει ένα αρχείο πηγαίου κώδικα Perl σαν να ήταν τμήμα του τρέχοντος προγράμματος.
Η do φοράει δύο άσχετα καπέλα που μοιράζονται μόνο μια δεσμευμένη λέξη. Το do BLOCK ομαδοποιεί δηλώσεις σε μία ενιαία έκφραση και επιστρέφει την τιμή της τελευταίας - συντακτικό εργαλείο, όχι συνάρτηση. Το do EXPR μεταχειρίζεται τη συμβολοσειρά ως όνομα αρχείου και μεταγλωττίζει-και-εκτελεί το αρχείο σε χρόνο εκτέλεσης. Διαβάστε όποια ενότητα ταιριάζει στη μορφή που βλέπετε· η ανάμειξή τους είναι η συνηθέστερη πηγή σύγχυσης γύρω από αυτή τη δεσμευμένη λέξη.
Σύνοψη#
do BLOCK # group statements, yield last value
do EXPR # load and run a Perl file
do { ... } while CONDITION; # post-test loop idiom
Τι επιστρέφεται#
do BLOCK- η τιμή της τελευταίας έκφρασης που αποτιμήθηκε στο μπλοκ, σε όποιο περιβάλλον επιβάλλει η περιβάλλουσα έκφραση.do EXPR- η τιμή της τελευταίας έκφρασης που αποτιμήθηκε στο αρχείο σε επιτυχία·undefσε αποτυχία, με την$@τεθειμένη στο σφάλμα μεταγλώττισης ή την$!τεθειμένη στο σφάλμα I/O.
do BLOCK - ομαδοποίηση δηλώσεων σε μία έκφραση#
Το do BLOCK δεν είναι συνάρτηση. Είναι συντακτική κατασκευή που μετατρέπει μια ακολουθία δηλώσεων οριοθετημένη από αγκύλες σε έναν ενιαίο όρο, του οποίου η τιμή είναι η τιμή της τελευταίας δήλωσης. Καταφύγετε σε αυτή όταν η γλώσσα ζητά έκφραση αλλά έχετε αρκετές δηλώσεις να εκτελέσετε:
my $x = do {
my $tmp = fetch_raw();
$tmp =~ s/\s+\z//;
lc $tmp;
}; # $x = cleaned, lower-cased value
Όταν το do BLOCK τροποποιείται από επόμενο while ή until, το μπλοκ εκτελείται μία φορά πριν ελεγχθεί η συνθήκη - αυτό είναι το καθιερωμένο ιδίωμα βρόχου με μετα-έλεγχο:
do {
$line = <$fh>;
} while defined $line && $line !~ /^END\b/;
Σε οποιαδήποτε άλλη δήλωση, οι while και until ως τροποποιητές δήλωσης ελέγχουν τη συνθήκη πρώτα. Η do αποτελεί την εξαίρεση.
Οι next, last, redo δεν λειτουργούν μέσα σε do BLOCK#
Ένα do BLOCK δεν είναι βρόχος, ακόμη και όταν ακολουθείται από while ή until. Οι δεσμευμένες λέξεις ελέγχου βρόχου δεν το βλέπουν:
do {
last if $done; # WRONG - not a loop
} while $more;
Αν χρειάζεστε έλεγχο βρόχου, τυλίξτε το σώμα σε γυμνό μπλοκ, που είναι εμβέλεια προσβάσιμη σε βρόχο:
{
last if $done;
redo if $retry;
}
Ή χρησιμοποιήστε ρητό βρόχο while.
return μέσα σε γυμνό do BLOCK#
Η return μέσα σε do BLOCK επιστρέφει από την περικλείουσα υπορουτίνα, όχι από το ίδιο το μπλοκ. Αυτό είναι ενίοτε αυτό που θέλετε και ενίοτε σφάλμα - όπως και να έχει, να ξέρετε ποιο από τα δύο γράφετε:
sub classify {
my $n = shift;
my $kind = do {
return "zero" if $n == 0; # returns from classify(), not the do
$n > 0 ? "pos" : "neg";
};
"$kind ($n)";
}
Για να αποδώσετε μια τιμή από το μπλοκ χωρίς να επιστρέψετε από την υπορουτίνα, αφήστε απλώς την τελευταία έκφραση να είναι η τιμή - αυτό είναι όλο το νόημα του do BLOCK.
do EXPR - φόρτωση και εκτέλεση αρχείου πηγαίου κώδικα Perl#
Το do EXPR αποτιμά την EXPR, μεταχειρίζεται το αποτέλεσμα ως όνομα αρχείου, διαβάζει το αρχείο, το μεταγλωττίζει και το εκτελεί στο τρέχον πακέτο. Είναι παρόμοιο, αλλά όχι ακριβώς ίδιο, με
eval `cat stat.pl`;
Η μορφή do δεν τρέχει εξωτερική διεργασία, διατηρεί το αρχικό όνομα αρχείου για τα μηνύματα σφαλμάτων, και δεν μπορεί να δει λεκτικές μεταβλητές της περικλείουσας εμβέλειας - κάτι που το eval STRING μπορεί. Όπως η eval STRING, το αρχείο επαναναλύεται σε κάθε κλήση, οπότε do FILE μέσα σε βρόχο είναι σχεδόν πάντα λάθος.
Επίλυση διαδρομής και @INC#
Το όνομα αρχείου ερμηνεύεται ως εξής:
Απόλυτες διαδρομές (
/foo/stat.pl), διαδρομές που ξεκινούν με./, και διαδρομές που ξεκινούν με../χρησιμοποιούνται ως έχουν.Κάθε άλλη σχετική διαδρομή αναζητείται κατά μήκος του
@INC, και σε επιτυχία η διαδρομή που βρέθηκε καταγράφεται στο%INCυπό το κλειδί που διαβιβάσατε.
do '/etc/myapp/stat.pl'; # exact file
do './stat.pl'; # exact file, current directory
do 'stat.pl'; # search @INC
do 'MyApp/config.pl'; # search @INC
Από την Perl 5.26 και μετά, το . δεν βρίσκεται προεπιλεγμένα στο @INC, άρα ένα γυμνό do 'stat.pl' δεν επαναφέρεται πλέον στον τρέχοντα κατάλογο. Αν το αρχείο δεν βρεθεί, η Perl εκπέμπει την υπόδειξη:
do "stat.pl" failed, '.' is no longer in @INC;
did you mean do "./stat.pl"?
Διαβιβάστε ./stat.pl όταν εννοείτε τον τρέχοντα κατάλογο.
Τιμή επιστροφής και αναφορά σφαλμάτων#
Το do EXPR έχει τρία αποτελέσματα που πρέπει να διακρίνετε:
Αποτέλεσμα | Επιστροφή | ||
|---|---|---|---|
Αρχείο μεταγλωττίστηκε και εκτελέστηκε | τιμή της τελευταίας έκφρασης |
| αμετάβλητη |
Αρχείο διαβάστηκε αλλά δεν μεταγλωττίστηκε | σφάλμα μεταγλώττισης | ενδέχεται επίσης να τεθεί | |
Αρχείο δεν μπόρεσε να διαβαστεί |
| σφάλμα I/O |
Επειδή η αποτυχία μεταγλώττισης μπορεί παρεμπιπτόντως να θέσει και την $!, ελέγχετε πάντα πρώτα την $@:
my $return;
for my $file ("/etc/myapp.rc", "$ENV{HOME}/.myapprc") {
unless ($return = do $file) {
warn "couldn't parse $file: $@" if $@;
warn "couldn't do $file: $!" unless defined $return;
warn "couldn't run $file" unless $return;
}
}
Οι τρεις warn αντιστοιχούν στα τρία σχήματα αποτυχίας παραπάνω συν την τέταρτη περίπτωση - το αρχείο εκτελέστηκε αλλά η τελευταία του έκφραση ήταν ψευδής.
Γιατί να μη χρησιμοποιείτε do FILE για αρθρώματα#
Για τη φόρτωση κώδικα βιβλιοθήκης, χρησιμοποιήστε require ή use αντί για do EXPR. Η require αποθηκεύει στην προσωρινή μνήμη μέσω του %INC (ώστε ένα αρχείο να φορτώνεται το πολύ μία φορά), εγείρει εξαίρεση σε αποτυχία αντί να επιστρέφει undef, και ολοκληρώνεται με τους γάντζους του @INC. Η use προσθέτει επιπλέον φόρτωση κατά τη μεταγλώττιση και χειρισμό εισαγωγής. Το do EXPR υπάρχει για αρχεία ρυθμίσεων και ad-hoc ενσωμάτωση σεναρίων - όχι για βιβλιοθήκες.
Παραδείγματα#
Ομαδοποίηση δηλώσεων σε έκφραση:
my $config = do {
open my $fh, "<", $path or die $!;
local $/;
<$fh>;
}; # $config = whole file as one string
Βρόχος μετα-ελέγχου με do … while:
my $n;
do {
print "enter a positive number: ";
chomp($n = <STDIN>);
} until defined $n && $n =~ /\A\d+\z/ && $n > 0;
Ανάγνωση αρχείου ρυθμίσεων σε σύνταξη Perl, με πλήρη τριμερή χειρισμό σφαλμάτων:
my $cfg = do '/etc/myapp/config.pl';
if (!defined $cfg) {
die $@ ? "config parse error: $@"
: "config read error: $!";
}
die "config did not return a true value" unless $cfg;
Υπό συνθήκη επιλογή τιμής όπου κάθε διακλάδωση αποτελείται από αρκετές δηλώσεις:
my $greeting = do {
if ($user) {
my $name = $user->display_name;
"hello, $name";
}
else {
"hello, stranger";
}
};
Προσωρινή τοπικοποίηση που πρέπει να καλύπτει αρκετές δηλώσεις και να αποδίδει και τιμή:
my $dump = do {
local $Data::Dumper::Sortkeys = 1;
local $Data::Dumper::Indent = 1;
Data::Dumper::Dumper(\%state);
};
Καθολική κατάσταση που επηρεάζει#
@INC- αναζητείται όταν ηdo EXPRκαλείται με σχετική διαδρομή διαφορετική από./…ή../….%INC- ενημερώνεται με τη διαδρομή που επιλύθηκε όταν ηdo EXPRπετυχαίνει σε αναζήτηση στο@INC.$@- τίθεται από τηνdo EXPRσε αποτυχία μεταγλώττισης· καθαρίζεται σε επιτυχία.$!- τίθεται από τηνdo EXPRσε αποτυχία I/O (το αρχείο δεν μπόρεσε να διαβαστεί).$0,__FILE__,__LINE__- το τρέχον υπό μεταγλώττιση όνομα αρχείου τίθεται στο αρχείο που φορτώνεται όσο ηdo EXPRτο αναλύει, ώστε οι προειδοποιήσεις και τα μηνύματαdieνα δείχνουν στο σωστό αρχείο.
Το do BLOCK δεν επηρεάζει καθολικές μεταβλητές· είναι καθαρά συντακτική κατασκευή.
Οριακές περιπτώσεις#
Το
do BLOCKδεν είναι βρόχος. Οιnext,lastκαιredoμέσα σεdo … whileδεν αφορούν το μπλοκ. Χρησιμοποιήστε γυμνό μπλοκ ή πραγματικό βρόχοwhile.Η
returnμέσα σεdo BLOCKεπιστρέφει από την περικλείουσα υπορουτίνα. Όχι από το μπλοκ. Δεν υπάρχει τρόπος να επιστραφεί τιμή απόdo BLOCKπέρα από το να γίνει η τελευταία αποτιμούμενη έκφραση.Οι τροποποιητές δήλωσης ελέγχουν εκ των προτέρων παντού αλλού. Το
STATEMENT while CONDελέγχει πρώτα· τοdo BLOCK while CONDελέγχει μετά την πρώτη εκτέλεση. Αυτή είναι σκόπιμη εξαίρεση της γλώσσας.Σχετικές διαδρομές χωρίς
./αναζητούν στο@INC. Από την 5.26, το.δεν είναι στο@INC, οπότεdo 'stat.pl'δεν σημαίνει πια «αρχείο τρέχοντος καταλόγου»· διαβιβάστε./stat.plαν αυτή ήταν η πρόθεση.Το
do EXPRδεν μπορεί να δει περικλείουσες λεκτικές μεταβλητές. Το αρχείο αναλύεται σαν να βρίσκεται στο ανώτατο επίπεδο. Αν χρειάζεστε πρόσβαση σε λεκτικές μεταβλητές, χρησιμοποιήστεeval STRING- με τις συνήθεις επιφυλάξεις περί taint και injection.Το αρχείο επαναναλύεται σε κάθε κλήση. Η
do EXPRδεν αποθηκεύεται προσωρινά μέσω του%INCμε τον τρόπο που είναι ηrequire. Κάλεσμα μέσα σε βρόχο επαναδιαβάζει και επαναμεταγλωττίζει το αρχείο σε κάθε επανάληψη.Ελέγχετε την
$@πριν την$!. Μια αποτυχία μεταγλώττισης μπορεί επίσης να θέσει την$!ως παρενέργεια· το σφάλμα μεταγλώττισης στην$@είναι αυτό που πραγματικά θέλετε να αναφέρετε.Ένα αρχείο που μεταγλωττίζεται αλλά επιστρέφει ψευδή τιμή είναι αμφίσημο. Το
unless ($ret = do $file) { … }πυροδοτείται τόσο για «το αρχείο απέτυχε» όσο και για «το αρχείο εκτελέστηκε και επέστρεψε0». Αν επιτρέπεται το αρχείο ρυθμίσεών σας να καταλήγει σε ψευδή τιμή, χρησιμοποιήστε ρητά τηνdefinedκαι την$@αντί να ελέγχετε την αλήθεια της επιστροφής.doχωρίς όρισμα είναι συντακτικό σφάλμα. Και οι δύο μορφές απαιτούν έναν όρο: ένα μπλοκ ή μια έκφραση.
Διαφορές από το upstream#
Πλήρως συμβατό με το upstream Perl 5.42.
Δείτε επίσης#
eval- ηeval BLOCKπαγιδεύει εξαιρέσεις χωρίς να διαβάζει αρχείο· ηeval STRINGαναλύει αυθαίρετο κώδικα και μπορεί να δει περικλείουσες λεκτικές μεταβλητές, σε αντίθεση με τηνdo EXPRrequire- φορτώνει αρχείο βιβλιοθήκης με προσωρινή αποθήκευση μέσω%INCκαι αυτόματη εξαίρεση σε αποτυχία· το σωστό εργαλείο για αρθρώματαreturn- σημειώστε ότι ηreturnμέσα σεdo BLOCKεπιστρέφει από την περικλείουσα υπορουτίνα, όχι από το μπλοκ@INC- διαδρομή αναζήτησης αρθρωμάτων που συμβουλεύεται ηdo EXPRσε σχετικά ονόματα αρχείων%INC- καταγράφει τη διαδρομή που επιλύθηκε μετά από μια επιτυχημένηdo EXPRμε αναζήτηση στο@INC$@- σφάλμα κατά τη μεταγλώττιση απόdo EXPR$!- σφάλμα I/O απόdo EXPRόταν το αρχείο δεν μπορεί να διαβαστεί