Ορίσματα και @_#

Όταν μια sub καλείται με τον κλασικό τρόπο (χωρίς υπογραφή), η Perl ισοπεδώνει ολόκληρη τη λίστα ορισμάτων σε έναν ενιαίο πίνακα και τον εκθέτει μέσα στο σώμα ως @_. Τα στοιχεία αυτού του πίνακα δεν είναι αντίγραφα - είναι ψευδώνυμα προς τις εκφράσεις του καλούντος. Αυτός ο ενιαίος κανόνας είναι η πηγή των περισσότερων εκπλήξεων της σύμβασης κλήσης της Perl και του μεγαλύτερου μέρους της ισχύος της.

Ο κανόνας ψευδωνυμοποίησης#

sub bump {
    $_[0]++;                     # mutates the caller's variable
}

my $x = 10;
bump($x);
say $x;                          # 11

Η τροποποίηση του $_[0] γράφει μέσω του ψευδωνύμου σε ό,τι πέρασε ο καλών. Αυτό γίνεται σκοπίμως - είναι ο τρόπος με τον οποίο η chomp(@lines) αφαιρεί newlines από κάθε στοιχείο του @lines αντί από αντίγραφο του @lines.

Η ψευδωνυμοποίηση επεκτείνεται και στα κυριολεκτικά, με προβλέψιμες συνέπειες:

bump(10);                        # error: Modification of a read-only
                                 # value attempted

Ένα κυριολεκτικό 10 είναι σταθερά. Το ψευδώνυμο δείχνει σε αυτή· η απόπειρα τροποποίησης αποτυγχάνει με die.

Το ιδίωμα αποσυσκευασίας#

Επειδή η ψευδωνυμοποίηση σπάνια είναι αυτό που θέλει το σώμα, σχεδόν κάθε sub κλασικού τύπου ξεκινά με ένα από αυτά τα δύο μοτίβα:

# Named arguments (most common)
sub greet {
    my ($name, $greeting) = @_;  # COPY into private lexicals
    $greeting //= 'Hello';
    return "$greeting, $name!";
}

# Method dispatch
sub method_call {
    my $self = shift;            # COPY first arg, leave rest in @_
    my %args = @_;               # remaining args as a hash
    ...
}

Μόλις αποσυσκευαστούν, το σώμα δουλεύει με ιδιωτικά αντίγραφα και δεν μπορεί να τροποποιήσει κατά λάθος την κατάσταση του καλούντος. Η shift χωρίς όρισμα μέσα σε sub χρησιμοποιεί εξ ορισμού το @_, γι” αυτό η my $self = shift; υπάρχει παντού στην OO Perl.

Νόμιμες χρήσεις ψευδωνυμοποίησης#

Η ψευδωνυμοποίηση είναι το σωστό εργαλείο όταν πραγματικά θέλετε να τροποποιήσετε την κατάσταση του καλούντος:

# in-place trim
sub trim_inplace {
    for (@_) {
        s/^\s+//;
        s/\s+$//;
    }
}

my @data = ("  hello  ", "  world\n");
trim_inplace(@data);
# @data is now ("hello", "world")

Ο βρόχος for (@_) ψευδωνυμεί το $_ σε κάθε στοιχείο του @_, το οποίο είναι με τη σειρά του ψευδώνυμο προς κάθε στοιχείο του @data. Οι τροποποιήσεις s/// περνούν μέσα από τα δύο βήματα ψευδωνύμων στις αρχικές συμβολοσειρές. Χωρίς την ψευδωνυμοποίηση, ο ίδιος κώδικας θα ήταν διπλάσια δουλειά.

Ο ίδιος μηχανισμός τροφοδοτεί τα chomp(@lines), chop(@buf), και οποιαδήποτε συνάρτηση «επεξεργασία λίστας και επιτόπια τροποποίηση» γράφετε εσείς. Δηλώστε την πρόθεση στο όνομα της συνάρτησης (_inplace, mutate_, παρόμοια) ώστε οι καλούντες να μην αιφνιδιαστούν.

Ονομαστικά ορίσματα#

Η Perl δεν έχει ενσωματωμένη σύνταξη ονομαστικών παραμέτρων στην κλασική μορφή, αλλά ο => (fat comma) σε συνδυασμό με ανάθεση hash δίνει το ίδιο αποτέλεσμα:

sub configure {
    my %opt = @_;                # ('host' => 'localhost', 'port' => 8080)
    $opt{host} //= 'localhost';
    $opt{port} //= 80;
    ...
}

configure( host => 'example.com', port => 8080 );

Ο fat comma είναι σημασιολογικά ίδιος με ένα κανονικό κόμμα αλλά περικλείει σε εισαγωγικά το bareword στα αριστερά του, οπότε host => ... είναι συντομογραφία για 'host' => .... Δείτε , για τον ίδιο τον τελεστή.

Για υποχρεωτικά-και-προαιρετικά ονομαστικά ορίσματα, η τυπική μορφή είναι:

sub render {
    my ($template, %opt) = @_;   # one positional, rest as named
    $opt{escape} //= 1;
    ...
}

render('greeting.tt', escape => 0, locale => 'de');

Για το σύγχρονο ιδίωμα ονομαστικών παραμέτρων βασισμένο σε υπογραφές, δείτε υπογραφές.

shift, pop, unshift, push στο @_#

Το @_ είναι ένας πραγματικός πίνακας. Μπορείτε να κάνετε shift από την αρχή, pop από το τέλος, τομή, τροποποίηση. Τίποτα από αυτά δεν αλλάζει την κατάσταση του καλούντος - μόνο η εγγραφή μέσω $_[N] το κάνει.

sub describe {
    my $what = shift;            # remove first arg from @_
    my @rest = @_;               # copy what's left
    ...
}

sub method {
    my $self = shift;
    my @args = @_;
    $self->dispatch(@args);
}

Μια πρακτική συνέπεια: αν θέλετε να ξανα-πακετάρετε τα εναπομείναντα ορίσματα για προώθηση σε άλλη sub, απλώς γράψτε @_ - ακριβώς αυτό κάνει η goto &other χωρίς ρητό πέρασμα. Δείτε αναδρομή για προώθηση κλήσης ουράς.

wantarray και το περιβάλλον κλήσης#

Μέσα σε μια sub, η wantarray σας λέει το περιβάλλον στο οποίο έγινε η κλήση:

wantarray

Περιβάλλον

Τυπική μορφή

1 (αληθές)

λίστα

επιστροφή λίστας

0 (ψευδές)

scalar

επιστροφή ενός βαθμωτού

undef

κενό

πρόωρη επιστροφή· το αποτέλεσμα θα απορριφθεί

sub items {
    return unless defined wantarray;     # void: nothing to compute
    my @result = compute_items();
    return wantarray ? @result : scalar @result;
}

Το @_ λέει τι ήρθε· η wantarray λέει τι αναμένεται πίσω. Είναι τα δύο μισά της εικόνας της σύμβασης κλήσης. Δείτε lvalue και περιβάλλον για πλήρη συζήτηση.

Συνήθεις παγίδες#

  • my ($x) = @_ έναντι my $x = @_. Το πρώτο είναι περιβάλλον λίστας: η $x παίρνει το πρώτο όρισμα. Το δεύτερο είναι βαθμωτό περιβάλλον: η $x παίρνει τον αριθμό ορισμάτων. Οι παρενθέσεις στα αριστερά κάνουν τη διαφορά· τα λάθη εδώ είναι συχνά.

    sub one_arg {
        my ($x) = @_;            # $x = first arg
    }
    
    sub count_args {
        my $n = @_;              # $n = number of args
    }
    
  • Τροποποίηση $_[N] «κατά λάθος». Συναρτήσεις όπως η chomp, chop, tr///, s/// λειτουργούν στο όρισμά τους επιτόπια. Η chomp($_[0]) τροποποιεί τη συμβολοσειρά του καλούντος. Αν δεν είναι αυτό που θέλετε, αντιγράψτε πρώτα.

  • Ισοπέδωση πινάκων και hashes. Πίνακες και hashes που περνιούνται ως ορίσματα χάνουν την ταυτότητά τους στο σημείο κλήσης - γίνονται όλα στοιχεία του ενιαίου επίπεδου @_. Για να περάσετε πίνακα χωρίς ισοπέδωση, περάστε αναφορά:

    takes_array(\@arr);          # one ref argument
    sub takes_array {
        my ($aref) = @_;
        push @$aref, 'extra';
    }
    
  • @_ μετά από goto &sub. Το τρέχον @_ περνιέται στον στόχο. Ό,τι έχετε ήδη αφαιρέσει με shift δεν υπάρχει πλέον από την πλευρά του στόχου. Αυτό είναι χαρακτηριστικό, όχι σφάλμα - δείτε αναδρομή.

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

  • @_ - η εγγραφή perlvar της μεταβλητής, με τον κανόνα ψευδωνυμοποίησης και τη συζήτηση περιβάλλοντος βρόχου.

  • shift, pop - χρησιμοποιούν εξ ορισμού το @_ μέσα σε sub.

  • Υπογραφές - η σύγχρονη εναλλακτική για χειρισμό παραμέτρων κατά θέση και ονομαστικών.

  • Τιμές επιστροφής - τι στέλνει πίσω το σώμα.

  • Τελεστής κόμματος - => για κατασκευή ονομαστικών ορισμάτων.