Lvalue subs και περιβάλλον#

Δύο σχετικά θέματα που εμφανίζονται όπου μια sub χρειάζεται να συμπεριφέρεται διαφορετικά ανάλογα με τον τρόπο χρήσης της: subs :lvalue (αυτές που μπορούν να χρησιμοποιηθούν στην αριστερή πλευρά μιας ανάθεσης) και αποστολή βασισμένη στο wantarray ανάλογα με το περιβάλλον του καλούντος.

Γιατί το περιβάλλον έχει σημασία στις subs#

Κάθε έκφραση Perl αποτιμάται σε ένα περιβάλλον - λίστα, βαθμωτό (με υπο-ποικιλίες: αριθμητικό, συμβολοσειράς, λογικό), ή κενό - που επιβάλλεται από αυτό που την περιβάλλει. Μια κλήση sub κληρονομεί το περιβάλλον της θέσης της:

my @r = my_sub();                # list context
my $r = my_sub();                # scalar context
my_sub();                        # void context
print my_sub();                  # list context (print's args are LIST)
my $n = () = my_sub();           # scalar of (a list assignment) - element count

Για τις περισσότερες subs, αυτό είναι αόρατο - επιστρέφουν τα ίδια δεδομένα ούτως ή άλλως και η αυτόματη μετατροπή περιβάλλοντος της Perl κάνει τη σωστή δουλειά. Για κάποιες subs η δουλειά είναι πραγματικά διαφορετική ανάλογα με το περιβάλλον, και αυτές οι subs ωφελούνται από ρητή εξέταση της wantarray.

wantarray: ο τριμερής διακόπτης#

sub items {
    if (wantarray) {
        return (1, 2, 3);                  # list context
    }
    elsif (defined wantarray) {
        return 3;                          # scalar context
    }
    else {
        return;                            # void context - caller will
                                           # discard whatever we return
    }
}

Οι τρεις τιμές επιστροφής της wantarray:

Τιμή επιστροφής

Περιβάλλον του καλούντος

Πρακτική σημασία

1 (αληθές)

λίστα

επιστροφή όλων

0 (ψευδές)

scalar

επιστροφή ενός πράγματος (π.χ. αριθμός)

undef

κενό

κανέναν δεν ενδιαφέρει· σκεφτείτε πρόωρη επιστροφή

Η defined wantarray είναι ο έλεγχος για «βαθμωτό ή λίστα» (οτιδήποτε μη κενό).

Η συντόμευση κενού περιβάλλοντος#

Όταν η εργασία είναι ακριβή και ο καλών πέταξε το αποτέλεσμα, επιστρέψτε πρόωρα:

sub expensive_query {
    return unless defined wantarray;       # nothing to compute
    my @rows = $db->select(...);           # actually do the work
    return wantarray ? @rows : scalar @rows;
}

expensive_query();                         # void: returns immediately
my @r = expensive_query();                 # list: full result
my $n = expensive_query();                 # scalar: count

Αυτό το μοτίβο είναι ο κορυφαίος λόγος να συμβουλευτείτε τη wantarray: μετατροπή μιας sub «κάνε πάντα τη δουλειά» σε sub «κάνε τη δουλειά μόνο αν ο καλών τη θέλει».

Πότε δεν πρέπει να χρησιμοποιήσετε wantarray#

  • Για υπερφόρτωση ευκολίας. Μια sub που επιστρέφει hashref σε βαθμωτό περιβάλλον και hash σε περιβάλλον λίστας είναι κίνδυνος συντήρησης - οι αναγνώστες δεν μπορούν να καταλάβουν από το σημείο κλήσης τι παίρνουν. Διαλέξτε ένα.

  • Για subs που είναι προφανώς τύπου-λίστας ή τύπου-βαθμωτού. Μια sub με όνομα count_users πρέπει να επιστρέφει αριθμό· οι αναγνώστες δεν περιμένουν να συμπεριφέρεται διαφορετικά σε περιβάλλον λίστας.

Subs :lvalue#

Μια sub :lvalue είναι αυτή της οποίας η τιμή επιστροφής μπορεί η ίδια να λάβει ανάθεση. Το σώμα πρέπει να τελειώνει σε μια έκφραση που έχει θέση μνήμης - τυπικά μια λεξιλογική my ή στοιχείο συγκεντρωτικού:

sub editable :lvalue {
    my $self = shift;
    $self->{counter};                      # last expression is an lvalue
}

my $obj = { counter => 0 };
bless $obj, 'Counter';

$obj->editable = 42;                       # writes through to $obj->{counter}

Η τελευταία έκφραση του σώματος είναι αυτό στο οποίο γράφει η ανάθεση. Αν η έκφραση δεν είναι lvalue (return $x + 1, return scalar @list), η ανάθεση είναι παράνομη και θα λάβετε ξεκάθαρο σφάλμα.

Η :lvalue σπάνια είναι το σωστό εργαλείο. Τα μόνα σημεία όπου αποδίδει:

  • Tied μεταβλητές και περιτυλίγματα τύπου tied, όπου η sub μεσολαβεί στην πρόσβαση σε πραγματικό lvalue από κάτω.

  • DSL που πραγματικά θέλουν σύνταξη ανάθεσης για ρύθμιση (π.χ. $config->host = 'localhost';).

Για «θέσε μια ιδιότητα σε αντικείμενο», ένας απλός setter είναι πιο ξεκάθαρος:

sub set_counter {
    my ($self, $value) = @_;
    $self->{counter} = $value;
    return $self;                           # method chaining
}

$obj->set_counter(42);                     # explicit, no surprises

:lvalue και η δομή του σώματος#

Επειδή η τελευταία έκφραση του σώματος είναι ειδική, οι κανόνες είναι περιοριστικοί:

sub good :lvalue { $self->{x} }            # OK - last expr is lvalue

sub also_good :lvalue {
    my @scratch = ...;                     # work
    $self->{x};                            # last expr is lvalue
}

sub bad :lvalue {
    return $self->{x};                     # NOT OK - explicit return
                                           # disables :lvalue
}

sub also_bad :lvalue {
    if ($cond) { $self->{x} }
    else       { $self->{y} }              # an `if` block is not an lvalue
}

Ο κανόνας «χωρίς ρητό return» είναι ο ίδιος που κάνει τα πρωτότυπα σταθεροποίησης να λειτουργούν· και τα δύο βασίζονται στο ότι το σώμα τελειώνει με μία μόνη έκφραση που παράγει τιμή.

Για τη σύγχρονη εναλλακτική - ένας setter που επιστρέφει $self για αλυσίδωση - δείτε τον οδηγό αντικειμενοστραφούς προγραμματισμού.

Συνδυασμός :lvalue με περιβάλλον#

Μια sub :lvalue δεν μπορεί εύκολα να διακλαδιστεί βάσει wantarray, επειδή η lvalue εκδοχή της κλήσης δεν ταιριάζει ακριβώς στο μοντέλο «επιστρέφει μια τιμή». Αν θέλετε και τις δύο συμπεριφορές από μία sub, αποδεχτείτε ότι βρίσκεστε σε περιοχή DSL και τεκμηριώστε προσεκτικά τη σύμβαση - ή χωρίστε σε δύο subs.

Τελεστές που επηρεάζουν το περιβάλλον#

Μερικοί τελεστές επιβάλλουν συγκεκριμένο περιβάλλον στο όρισμά τους· η sub θα δει αυτό το περιβάλλον ανεξάρτητα από το πού κατευθύνεται η περιβάλλουσα έκφραση:

my @r = scalar my_sub();         # scalar() forces scalar context on my_sub
my $n = () = my_sub();           # the inner () = ... is list context;
                                 # outer assignment to $n is scalar of that

Το ιδίωμα () = LIST είναι ο τυπικός τρόπος για επιβολή περιβάλλοντος λίστας σε κλήση sub και στη συνέχεια ανάγνωση του πλήθους. Δείτε ανάθεση για τη συζήτηση σε επίπεδο τελεστή.

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

  • wantarray - η δεσμευμένη λέξη.

  • Τιμές επιστροφής - η υπόλοιπη εικόνα τιμών επιστροφής (χωρίς την ανατροπή lvalue).

  • Γνωρίσματα - η :lvalue είναι ένα από πολλά ενσωματωμένα γνωρίσματα.

  • Τελεστής ανάθεσης - τι κάνει πραγματικά η = του καλούντος σε κάθε πλευρά μιας κλήσης :lvalue.

  • scalar - ρητός εξαναγκασμός περιβάλλοντος.