Ροή ελέγχου

wantarray#

Αναφέρει το περιβάλλον κλήσης της υπορουτίνας που εκτελείται τη δεδομένη στιγμή.

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

Σύνοψη#

wantarray

Δεν λαμβάνει ορίσματα και δεν έχει μορφή με παρενθέσεις - είναι δεσμευμένη λέξη, όχι κλήση συνάρτησης.

Τι επιστρέφεται#

Η wantarray επιστρέφει μία από ακριβώς τρεις τιμές, που αντιστοιχούν στα τρία περιβάλλοντα που διακρίνει η Perl:

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

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

Τυπική σύνταξη καλούντος

αληθές (1)

λίστα

my @x = f(); (f())[0]

ψευδές ("")

βαθμωτό

my $x = f(); if (f()) {…}

undef

κενό

f(); ως εντολή

Η τριμερής διάκριση είναι ο λόγος που η συνάρτηση «θα έπρεπε να είχε ονομαστεί wantlist()»: η φαινομενικά λογική τιμή επιστροφής είναι στην πραγματικότητα τριών καταστάσεων, και το defined wantarray είναι ο σωστός έλεγχος για το «θέλησε καθόλου τίποτα ο καλών».

Ο κανονικός ιδιωματισμός#

Το μοτίβο για το οποίο υπάρχει η wantarray είναι υπορουτίνα που προσαρμόζει το σχήμα επιστροφής της στον καλούντα:

sub records {
    my @rows = heavy_query();
    return wantarray ? @rows : "@rows";
}

my @r = records();          # gets the list
my $s = records();          # gets a space-joined string
records();                  # still runs heavy_query - see below

Συνδυάστε το με πρόωρη έξοδο όταν ο καλών δεν ζήτησε τίποτα:

sub records {
    return unless defined wantarray;    # caller wrote `records();`
    my @rows = heavy_query();           # skipped in void context
    return wantarray ? @rows : "@rows";
}

Το defined wantarray είναι ο φύλακας κενού περιβάλλοντος. Η wantarray μόνη της δεν είναι - είναι ψευδής τόσο σε βαθμωτό όσο και σε κενό περιβάλλον, και πρόωρο return σε ψευδή wantarray θα έσπαζε κάθε καλούντα βαθμωτού περιβάλλοντος.

Το περιβάλλον διαδίδεται, δεν επανερμηνεύεται#

Η wantarray αναφέρει το περιβάλλον που επέβαλε ο καλών στην υπορουτίνα, όχι τι κάνει εσωτερικά η υπορουτίνα:

sub ctx { wantarray ? "list" : defined wantarray ? "scalar" : "void" }

my @a = ctx();      # "list"
my $s = ctx();      # "scalar"
ctx();              # "void"
print ctx(), "\n";  # "list"   - print's LIST is list context
scalar ctx();       # "scalar" - scalar() forces scalar context

Παρατηρήστε τα δύο τελευταία: η print επιβάλλει περιβάλλον λίστας στα ορίσματά της, και η scalar επιβάλλει ρητά βαθμωτό περιβάλλον. Κανένα δεν αφορά το τι κάνει η ctx με το αποτέλεσμα.

Κλήσεις από κενό περιβάλλον#

Μια υπορουτίνα που καλείται ως γυμνή εντολή εκτελείται σε κενό περιβάλλον, και η wantarray επιστρέφει undef μέσα σε εκείνη την υπορουτίνα. Αυτό δεν διαδίδεται προς τα κάτω: μια υπορουτίνα που καλείται μέσα από υπορουτίνα κενού περιβάλλοντος βλέπει όποιο περιβάλλον επέβαλε ο δικός της καλών.

sub inner { defined wantarray ? "wanted" : "void" }
sub outer { my $x = inner(); return $x }

outer();            # outer is void; inner is scalar → "wanted"

Κάθε σημείο κλήσης καθιερώνει το δικό του περιβάλλον ανεξάρτητα. Η wantarray αναφέρει πάντα το περιβάλλον της κοντινότερης περικλείουσας υπορουτίνας, όχι της δυναμικής αλυσίδας.

Λειτουργεί επίσης μέσα σε eval#

Η wantarray αναφέρει επίσης το περιβάλλον μπλοκ eval ή eval EXPR, όχι μόνο ονοματισμένων υπορουτινών:

my @x = eval { wantarray ? (1, 2, 3) : "scalar" };  # (1,2,3)
my $x = eval { wantarray ? (1, 2, 3) : "scalar" };  # "scalar"
eval { defined wantarray ? 1 : 0 };                 # void → 0

Είναι ο ίδιος μηχανισμός - το eval είναι πλαίσιο κλήσης με την ίδια έννοια που είναι μια υπορουτίνα.

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

Το αποτέλεσμα της wantarray είναι απροσδιόριστο σε αυτά τα μέρη, και ο κώδικας δεν πρέπει να βασίζεται σε κάποια συγκεκριμένη τιμή:

  • Στο ανώτατο επίπεδο ενός αρχείου (έξω από οποιαδήποτε υπορουτίνα).

  • Μέσα σε μπλοκ BEGIN, UNITCHECK, CHECK, INIT, ή END.

  • Μέσα σε μέθοδο DESTROY.

Αντιμετωπίστε την wantarray ως νοηματική μόνο μέσα σε συνηθισμένη υπορουτίνα ή eval. Οπουδήποτε αλλού, σχεδιάστε τον κώδικά σας ώστε η απάντηση να μην έχει σημασία.

Παραδείγματα#

Ένας getter που επιστρέφει την πλήρη λίστα σε καλούντες λίστας, το πλήθος σε καλούντες βαθμωτού, και δεν κάνει τίποτα σε κενό περιβάλλον:

sub warnings {
    return unless defined wantarray;
    my @w = collect_warnings();
    return wantarray ? @w : scalar @w;
}

my @all   = warnings();     # every warning
my $count = warnings();     # just the number
warnings();                 # collect_warnings() is not called

Μια υπορουτίνα που αρνείται να κληθεί σε κενό περιβάλλον επειδή η τιμή επιστροφής είναι όλο το νόημα:

sub must_use {
    defined wantarray
        or croak "must_use: return value must be used";
    return compute();
}

Μια υπορουτίνα που εκπέμπει ένα στοιχείο ανά κλήση σε βαθμωτό περιβάλλον και ολόκληρη τη δέσμη σε περιβάλλον λίστας - σχήμα που εκθέτουν ορισμένα APIs τύπου iterator:

sub next_batch {
    state @queue;
    @queue = refill() unless @queue;
    return wantarray ? splice(@queue) : shift @queue;
}

Οριακές περιπτώσεις#

  • Καμία παρένθεση, κανένα όρισμα. Το wantarray() αναλύεται αλλά οι κενές παρενθέσεις είναι θόρυβος· η δεσμευμένη λέξη δεν παίρνει τίποτα. Το wantarray $x είναι συντακτικό σφάλμα, όχι κλήση με όρισμα.

  • Όχι το περιβάλλον του καλούντος για τελεστές. Η wantarray αναφέρει μόνο το περιβάλλον πλαισίου κλήσης. Μια έκφραση όπως $sub->() + 1 βάζει την κλήση σε βαθμωτό περιβάλλον· η wantarray μέσα στο $sub βλέπει βαθμωτό, όπως αναμένεται. Δεν υπάρχει τρόπος να ρωτήσετε «κλήθηκα μέσα σε αριθμητική έκφραση;» - μόνο λίστα / βαθμωτό / κενό.

  • Η return τιμά το ίδιο περιβάλλον. Το return @list σε βαθμωτό περιβάλλον αποδίδει το τελευταίο στοιχείο, όχι το πλήθος - επειδή η ίδια η return βλέπει το περιβάλλον του καλούντος. Χρησιμοποιήστε return wantarray ? @list : scalar @list όταν θέλετε το πλήθος για καλούντες βαθμωτού.

  • Το λογικό περιβάλλον είναι βαθμωτό περιβάλλον. Το if (f()) καλεί την f σε βαθμωτό περιβάλλον· η wantarray εκεί επιστρέφει ψευδές, όχι undef.

  • Η ανάθεση λίστας σε κενή λίστα παραμένει περιβάλλον λίστας. Το () = f(); είναι περιβάλλον λίστας παρόλο που το αποτέλεσμα απορρίπτεται. Η wantarray επιστρέφει αληθές. Αυτό είναι χρήσιμο για επιβολή περιβάλλοντος λίστας σε υπορουτίνα της οποίας οι παρενέργειες εξαρτώνται από αυτό.

  • Η wantarray δεν είναι διαθέσιμη σε καλούντες XS με τον ίδιο τρόπο. Ένα XSUB ελέγχει το περιβάλλον μέσω του GIMME_V· μια υπορουτίνα Perl που καλείται από XSUB βλέπει το περιβάλλον που το XSUB καθιέρωσε για την κλήση.

  • Τα πρωτότυπα δεν αλλάζουν την wantarray. Ένα πρωτότυπο ($) επιβάλλει το περιβάλλον ενός ορίσματος, όχι το ίδιο το περιβάλλον κλήσης της υπορουτίνας.

Διαφορές από το upstream#

Πλήρως συμβατό με το upstream Perl 5.42.

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

  • return - τιμά το ίδιο περιβάλλον λίστας/βαθμωτού/κενού που αναφέρει η wantarray· τα δύο σχεδιάζονται για να χρησιμοποιούνται μαζί

  • caller - ελέγχει την ίδια τη στοίβα κλήσεων (πακέτο, αρχείο, γραμμή, και, στη μορφή τριών ορισμάτων, περισσότερες λεπτομέρειες περιβάλλοντος συμπεριλαμβανομένου του αν η wantarray θα επέστρεφε αληθές εκεί)

  • scalar - επιβάλλει βαθμωτό περιβάλλον σε μια έκφραση, που είναι αυτό που κάνει την wantarray να επιστρέφει ψευδές στον καλούμενο

  • eval - καθιερώνει πλαίσιο κλήσης του οποίου το περιβάλλον αναφέρει επίσης η wantarray