Περιβάλλον#
Σχεδόν κάθε τελεστής στην Perl αποτιμά τους τελεστέους του σε ένα συγκεκριμένο περιβάλλον - λίστας, βαθμωτό, κενό ή λογικό - και πολλοί τελεστές επιστρέφουν διαφορετικά πράγματα ανάλογα με το περιβάλλον στο οποίο κλήθηκαν. Η ίδια έκφραση μπορεί να σημαίνει διαφορετικά πράγματα σε διαφορετικές θέσεις:
my @arr = (10, 20, 30);
my @copy = @arr; # list context - three elements
my $len = @arr; # scalar context - 3
print @arr; # list context - "102030"
print "@arr"; # list context inside "" - "10 20 30"
print "" . @arr; # scalar context (concat) - "3"
@arr; # void context - discarded; warns under -w
Το περιβάλλον είναι η μεγαλύτερη μεμονωμένη διαφορά μεταξύ Perl και των περισσότερων άλλων γλωσσών, και η πιο συνηθισμένη πηγή εκπλήξεων «γιατί δεν κάνει αυτό που περιμένω».
Τα τέσσερα περιβάλλοντα#
Περιβάλλον | Τι βλέπει ο τελεστέος | Επιβάλλεται από |
|---|---|---|
λίστα | «δώσε μου όλες τις τιμές σου» |
|
βαθμωτό | «δώσε μου μία τιμή» |
|
κενό | «πέταξε το αποτέλεσμα» | δήλωση της οποίας η τιμή δεν χρησιμοποιείται |
λογικό | «δώσε μου αληθές/ψευδές» |
|
Το λογικό περιβάλλον είναι τεχνικά μια ειδική περίπτωση βαθμωτού περιβάλλοντος - ζητείται μία τιμή από τον τελεστέο, στη συνέχεια αυτή η τιμή ελέγχεται για αλήθεια. Τα δύο δεν είναι πάντα ίδια: μια υπορουτίνα που γνωρίζει wantarray μπορεί να ανιχνεύσει ξεχωριστά το λογικό περιβάλλον (δείτε παρακάτω).
Επιβολή περιβάλλοντος#
Η αριστερή πλευρά μιας ανάθεσης επιβάλλει περιβάλλον στη δεξιά:
my @x = func(); # func() is called in LIST context
my $x = func(); # func() is called in SCALAR context
my ($x, $y) = func(); # func() is called in LIST context
my ($x) = func(); # also LIST - note the parens
my $x = func(); # SCALAR - same chars, very different
Η πιο συνηθισμένη παγίδα είναι η my $x = func() έναντι my ($x) = func(). Χωρίς παρενθέσεις, η ανάθεση είναι βαθμωτή· με αυτές, είναι ανάθεση λίστας 1 στοιχείου. Αν η func() επιστρέφει τρία πράγματα σε περιβάλλον λίστας και «το πλήθος» σε βαθμωτό περιβάλλον, η διαφορά είναι μεταξύ 'first-thing' και 3.
Οι τελεστές επιβάλλουν επίσης περιβάλλον. Οι αριθμητικοί και συμβολοσειριακοί τελεστές επιβάλλουν βαθμωτό περιβάλλον στους τελεστέους τους· η print επιβάλλει λίστα:
my @arr = (1, 2, 3);
my $n = @arr + 0; # scalar context - 3 + 0 = 3
my $s = @arr . ""; # scalar context - "3"
print @arr; # list context - prints "123"
Και οι λογικοί έλεγχοι επιβάλλουν βαθμωτό (λογικό) περιβάλλον:
if (@arr) { ... } # true if @arr is non-empty
unless (%hash) { ... } # true if %hash is empty
while (<$fh>) { ... } # context is BOOLEAN, but the diamond op is
# special: it reads ONE line and assigns to $_
Τι δίνει ένας πίνακας σε βαθμωτό περιβάλλον#
Ένας πίνακας σε βαθμωτό περιβάλλον δίνει το μήκος του:
my @arr = (1, 2, 3);
my $n = @arr; # 3
Μια κυριολεκτική λίστα σε βαθμωτό περιβάλλον δίνει το τελευταίο στοιχείο της (ο τελεστής κόμματος):
my $x = (1, 2, 3); # 3 - comma operator, last element
my $x = ('a', 'b', 'c'); # 'c'
Αυτά μοιάζουν πανομοιότυπα αλλά συμπεριφέρονται διαφορετικά. Ο κανόνας είναι: οι πίνακες έχουν μήκος, οι κυριολεκτικές λίστες δεν έχουν. Μια σκέτη λίστα με κόμμα είναι ο τελεστής κόμματος της C και παράγει τον τελευταίο τελεστέο· μια μεταβλητή πίνακα σε βαθμωτή θέση ρωτάει «πόσα»:
my @a = ('x', 'y', 'z');
my $count = @a; # 3
my $last_el = $a[-1]; # 'z'
Ο τελεστής scalar επιβάλλει βαθμωτό περιβάλλον σε ό,τι ακολουθεί, κάτι που είναι ιδιαίτερα χρήσιμο όταν η θέση της έκφρασης είναι κατά τα άλλα αμφίσημη:
print "got " . scalar(@arr) . " items\n"; # "got 3 items"
print "got " . @arr . " items\n"; # same - concat imposes scalar
print "got @arr items\n"; # "got 1 2 3 items" - list inside ""
Τι δίνει ένα hash σε βαθμωτό περιβάλλον#
Ένα μη κενό hash σε βαθμωτό περιβάλλον επιστρέφει μια αληθή τιμή (το πλήθος κλειδιών, από Perl 5.25)· ένα κενό hash επιστρέφει την ψευδή τιμή 0. Η μορφή που έχει σημασία είναι η τιμή αλήθειας:
if (%h) { ... } # true if %h has any pairs
my $n = keys %h; # explicit: count of keys
my $n = %h; # also count of keys (Perl 5.25+); was a debug ratio earlier
Σε παλαιότερο κώδικα που στοχεύει perl πριν την 5.25 βλέπετε μερικές φορές ακόμα scalar keys %h· στην PetaPerl (στόχος perl 5.42) η scalar %h επιστρέφει το πλήθος κλειδιών και είναι μια χαρά.
wantarray - σύνταξη υπορουτινών ευαίσθητων στο περιβάλλον#
Μια υπορουτίνα μπορεί να ρωτήσει «σε ποιο περιβάλλον κλήθηκα;» με wantarray:
sub items {
return wantarray ? (1, 2, 3) # list context
: defined wantarray ? 3 # scalar context
: do { warn "items() called in void context\n"; () }; # void context
}
my @list = items(); # (1, 2, 3)
my $cnt = items(); # 3
items(); # void - warning fires
Η τριμερής διάκριση είναι ο μόνος τρόπος ανίχνευσης κενού περιβάλλοντος μέσα από μια υπορουτίνα. Χρησιμοποιήστε την με φειδώ: οι περισσότερες υπορουτίνες επιστρέφουν μία μορφή και αφήνουν τον καλούντα να ασχοληθεί με τον εξαναγκασμό περιβάλλοντος. Οι περιπτώσεις όπου η wantarray αξίζει τον κόπο είναι:
Συναρτήσεις που νόμιμα επιστρέφουν είτε «τη λίστα» είτε «το πλήθος» - π.χ. ρουτίνες αναζήτησης κειμένου.
Βοηθοί διαγνωστικών που εκτυπώνουν προειδοποίηση όταν το αποτέλεσμά τους απορρίφθηκε.
Μια υπορουτίνα που επιστρέφει διαφορετικές σημασίες (όχι απλώς διαφορετικές μορφές) βάσει wantarray είναι κίνδυνος για τη συντήρηση. Δύο κατονομασμένες συναρτήσεις διαβάζονται καλύτερα από μία πολυμορφική.
Παγίδα: scalar(@arr) έναντι @arr σε διαφορετικές θέσεις#
my @arr = (1, 2, 3);
# 1. Function arguments - LIST context
some_func(@arr); # passes 1, 2, 3 as three arguments
some_func(scalar @arr); # passes 3 as a single argument
# 2. String concatenation - SCALAR context
"got " . @arr # "got 3"
# 3. Inside double quotes - array interpolation, NOT scalar!
"got @arr" # "got 1 2 3"
# 4. Boolean test - SCALAR (boolean) context
if (@arr) { ... } # true iff @arr non-empty
# 5. Comparison - SCALAR context on both sides
@arr == 3 # 3 == 3 is true
@arr eq "3" # "3" eq "3" is true; this is rarely what you want
Η ασυμφωνία μεταξύ της περίπτωσης 2 (συνένωση → βαθμωτό) και της περίπτωσης 3 (μέσα σε "" → λίστα) είναι η πιο συχνά ερωτώμενη ερώτηση περιβάλλοντος στην Perl. Μέσα σε "", η @arr είναι παρεμβολή πίνακα· εκτός "", το περιβάλλον της περιβάλλουσας έκφρασης αποφασίζει.
Παγίδα: ισοπέδωση λίστας στα ορίσματα συναρτήσεων#
Μια συνάρτηση λαμβάνει τα @_ ως μία ενιαία επίπεδη λίστα όλων των ορισμάτων - δεν υπάρχει τρόπος η καλούμενη να ξέρει πού τελείωσε ο ένας πίνακας και πού ξεκίνησε ο επόμενος:
sub many {
print "got ", scalar @_, " args\n";
}
my @a = (1, 2);
my @b = (3, 4, 5);
many(@a, @b); # 5 args - both arrays flattened
many(\@a, \@b); # 2 args - two array references
many(scalar @a, scalar @b); # 2 args - two integers (the lengths)
Αυτός είναι ο λόγος που υπάρχουν οι αναφορές: η μεταβίβαση μιας μη επίπεδης μορφής μέσω της σύμβασης κλήσης που ισοπεδώνει λίστες. Οτιδήποτε θέλετε να διατηρήσει την ταυτότητά του μέσα στα @_ πρέπει να φτάσει ως αναφορά.
Πραγματικό παράδειγμα: μια υπορουτίνα που επιστρέφει πλήθος ή λίστα#
sub digits_of {
my ($s) = @_;
return $s =~ /\d/g; # in list context: all digit chars
# in scalar context: TRUE/FALSE (last match)
}
my @digits = digits_of("a1b2c3"); # ('1', '2', '3') - three matches
my $any = digits_of("a1b2c3"); # 1 - boolean
Για να κάνετε τη digits_of να επιστρέφει πλήθος σε βαθμωτό περιβάλλον, η δεξιά πλευρά πρέπει να εξαναγκάσει ρητά:
sub digits_of {
my ($s) = @_;
my @d = $s =~ /\d/g;
return wantarray ? @d : scalar @d;
}
my @digits = digits_of("a1b2c3"); # ('1', '2', '3')
my $n = digits_of("a1b2c3"); # 3
Αυτό είναι το κανονικό ιδίωμα «επιστροφή λίστας ή του μήκους της». Σημειώστε ότι χωρίς τον διακόπτη wantarray, η return @d θα έδινε το πλήθος σε βαθμωτό περιβάλλον ούτως ή άλλως (λόγω του κανόνα πίνακα-σε-βαθμωτό)
αλλά είναι πιο σαφές να είναι ρητό.
Δείτε επίσης#
Arrays - η τριχοτομία πίνακα-λίστας-βαθμωτού αναλυτικά.
Hashes -
%hσε βαθμωτό περιβάλλον.Comma - ο τελεστής που κατασκευάζει κυριολεκτικές λίστες και δίνει τον τελευταίο τελεστέο σε βαθμωτό περιβάλλον.
wantarray- ο τρόπος ερώτησης μέσα από υπορουτίνα.scalar- επιβολή βαθμωτού περιβάλλοντος.reverse- ευαίσθητη στο περιβάλλον: λίστα βαθμωτών σε περιβάλλον λίστας, μία αντεστραμμένη συμβολοσειρά σε βαθμωτό περιβάλλον.References - μεταβίβαση μιας μη επίπεδης μορφής μέσω της σύμβασης ορισμάτων σε περιβάλλον λίστας που ισοπεδώνει.