Αναφορές#

Μια αναφορά είναι ένα βαθμωτό που δείχνει σε μια άλλη τιμή. Οι αναφορές είναι ο τρόπος που η Perl κατασκευάζει εμφωλευμένες δομές δεδομένων - πίνακες πινάκων, hash από hashes, hash από πίνακες από hashes - επειδή οι βασικοί τύποι περιέκτη (πίνακες, hashes) ισοπεδώνονται όταν εμφανίζονται σε λίστες, και μια αναφορά είναι ένα μεμονωμένο βαθμωτό που επιβιώνει της ισοπέδωσης.

my @arr = (1, 2, 3);
my $ref = \@arr;             # take a reference to @arr
$$ref[0]                     # full deref form     - 1
$ref->[0]                    # arrow shorthand     - 1
@$ref                        # whole-array deref   - (1, 2, 3)

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

Πράγματα στα οποία μπορείτε να κάνετε αναφορά#

Επτά πράγματα δέχονται αναφορά:

\$scalar             # SCALAR ref
\@array              # ARRAY ref
\%hash               # HASH ref
\&subroutine         # CODE ref
\*glob               # GLOB ref
\\$scalar            # REF (a reference to a reference)
\$file_handle        # IO ref (reference to a filehandle)

Επιπλέον οι ανώνυμοι κατασκευαστές, που κατασκευάζουν την τιμή και επιστρέφουν αναφορά σε ένα βήμα:

my $aref = [1, 2, 3];                  # anonymous array
my $href = { name => 'Alice', age => 30 };  # anonymous hash
my $cref = sub { $_[0] * 2 };          # anonymous sub

Η διάκριση αγκυλών-παρενθέσεων είναι ουσιαστική: η (1, 2, 3) είναι μια λίστα, η [1, 2, 3] είναι μια ανώνυμη αναφορά πίνακα. Η πρώτη ισοπεδώνεται· η δεύτερη όχι.

Αποαναφορά#

Τέσσερις ορθογραφίες, σε αύξουσα σειρά συχνότητας εμφάνισης σε σύγχρονο κώδικα:

${$sref}             # full sigil-deref form
$$sref               # short sigil-deref form (only if name is simple)
$sref->{key}         # arrow form (postfix; for arrays and hashes)
$sref->[0]           # same, for arrays

Η μορφή βέλους -> είναι η ιδιωματική ορθογραφία για εμφωλευμένη πρόσβαση:

$config->{database}{host}            # arrow elision between subscripts
$config->{database}->{host}          # explicit; same meaning
${$config->{database}}{host}         # full form; the same again

Μεταξύ δύο διαδοχικών subscripts το -> είναι προαιρετικό (ο κανόνας «παράλειψης βέλους»)· στην αρχή μιας αλυσίδας είναι υποχρεωτικό:

$ref->[0]                     # required arrow at the head
$ref->[0][1]                  # second arrow elided
$ref->[0]->[1]                # equivalent
[1,2,3]->[0]                  # required arrow on an anonymous ref

Αποαναφορά ολόκληρου περιέκτη:

@$aref                        # the whole array
%$href                        # the whole hash, as flat key/value list
&$cref(@args)                 # call the sub
$cref->(@args)                # idiomatic call form

Χρησιμοποιήστε τη μορφή με αγκύλες @{ EXPR } / %{ EXPR } όταν ο στόχος αποαναφοράς είναι μια έκφραση πιο σύνθετη από μια σκέτη βαθμωτή μεταβλητή:

my @found = @{ $config->{users} };   # deref a chain - needs braces

Δείτε arrow για τον τελεστή και subscript για την οικογένεια αγκυλών.

Προτεραιότητα της αποαναφοράς με πρόθεμα έναντι της δεικτοδότησης#

Τα πέντε sigils αποαναφοράς με πρόθεμα $ @ * % & δένουν πιο σφιχτά από τις μεταθεματικές αγκύλες δεικτοδότησης [] και {}. Έτσι αυτά τα δύο είναι ακριβώς η ίδια έκφραση:

$$aref[2][2]                  # confusing, but valid
$aref->[2][2]                 # clear; identical meaning

Το $$aref[2][2] αποαναφέρει το $aref πρώτα, και κατόπιν δεικτοδοτεί το αποτέλεσμα. Δεν είναι η ανάγνωση της C *a[i], όπου η δεικτοδότηση θα εφαρμοζόταν πριν την αποαναφορά. Η Perl δεν έχει καμία κατασκευή που να δεικτοδοτεί πριν την αποαναφορά σε αυτή τη θέση· αν θέλετε «το πράγμα στο οποίο δείχνει το $i-οστό στοιχείο», γράψτε το αναλυτικά από αριστερά προς τα δεξιά με μια μεταθεματική αποαναφορά:

$aref->[$i]->$*               # deref the i-th element explicitly

Η μορφή με βέλος είναι η αναγνώσιμη επιλογή για εμφωλευμένη πρόσβαση· η μορφή με προπορευόμενο sigil είναι παγίδα ακριβώς επειδή διαβάζεται ως η προτεραιότητα της C και σημαίνει το αντίθετο. Δείτε την προτεραιότητα για τον πλήρη πίνακα τελεστών.

Τι επιστρέφει η ref#

Η ref αναφέρει το είδος πράγματος στο οποίο δείχνει μια αναφορά:

ref \$scalar         # 'SCALAR'
ref \@array          # 'ARRAY'
ref \%hash           # 'HASH'
ref \&sub            # 'CODE'
ref \*FH             # 'GLOB'
ref \\$scalar        # 'REF'    - a ref-to-ref
ref \$io_handle      # 'IO'     - wrapper around a file handle

ref [1, 2, 3]        # 'ARRAY'
ref { a => 1 }       # 'HASH'
ref sub {}           # 'CODE'

ref bless {}, 'Foo'  # 'Foo'    - blessed; class name not 'HASH'
ref 42               # ''       - not a reference at all
ref undef            # ''

Για blessed αντικείμενα, η ref επιστρέφει την κλάση. Για να διακρίνετε μια blessed αναφορά hash από μια απλή αναφορά hash, χρησιμοποιήστε Scalar::Util::reftype (που επιστρέφει πάντα τη μορφή του υποκείμενου περιέκτη ανεξαρτήτως bless) ή isa (ο έλεγχος με γνώση κληρονομικότητας).

Μεταθεματική αποαναφορά (@*, %*, $*)#

Η Perl 5.20+ πρόσθεσε μεταθεματική σύνταξη που αντικατοπτρίζει την αλυσίδα βέλους:

$ref->@*               # equivalent to @$ref
$ref->%*               # equivalent to %$ref
$ref->$*               # equivalent to $$ref

$ref->@[1, 3]          # array slice through ref - equivalent to @{$ref}[1,3]
$ref->%[1, 3]          # key/value slice
$ref->@{qw(a b)}       # hash slice

Οι μεταθεματικές μορφές διαβάζονται από αριστερά προς τα δεξιά μαζί με την υπόλοιπη αλυσίδα βέλους, κάτι που είναι συχνά πιο σαφές όταν ο στόχος αποαναφοράς είναι ο ίδιος αλυσίδα:

$config->{users}->@*             # all users - chains naturally
@{$config->{users}}              # same; reads right-to-left

Επιλέξτε τη μορφή που διαβάζεται καλύτερα στο εκάστοτε πλαίσιο. Η μεταθεματική είναι η πιο σαφής επιλογή σε αλυσίδες· η μορφή με αρχικό sigil είναι πιο σύντομη για απλές βαθμωτές αναφορές.

Αυτο-δημιουργία#

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

my %h;
$h{a}{b}{c} = 1;
# %h is now (a => { b => { c => 1 } })  - three nested hashes built

Ο μηχανισμός: όταν ο στόχος ανάθεσης είναι $h{a}{b}{c}, η Perl βλέπει ότι η $h{a} είναι undef, το επόμενο subscript είναι {b} (subscript hash), οπότε κάνει τη $h{a} νέα αναφορά hash. Στη συνέχεια η $h{a}{b} είναι undef, το επόμενο subscript είναι {c} (επίσης hash), οπότε την κάνει νέα αναφορά hash. Τέλος αναθέτει 1 στο φύλλο.

Το ίδιο συμβαίνει για subscripts πίνακα:

my %by_dept;
push @{ $by_dept{eng} }, 'Alice';
# $by_dept{eng} did not exist; the push autoviv'd it as an arrayref

Η αυτο-δημιουργία στα αριστερά μιας ανάθεσης (ή ενός μεταλλάκτη push/pop/…) είναι αυτό που κάνει αυτό να λειτουργεί. Η αυτο-δημιουργία στα δεξιά μιας έκφρασης είναι πιο επικίνδυνη - η ανάγνωση $h{a}{b}{c} όταν η $h{a} είναι undef επίσης αυτο-δημιουργεί, γεμίζοντας το hash ως παρενέργεια της εξέτασης:

my %h;
exists $h{a}{b};         # accidentally creates $h{a} = {} as a hash!
                          # %h is now (a => {})

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

exists $h{a} && exists $h{a}{b};        # short-circuits if $h{a} absent

Αυτή είναι η παγίδα αυτο-δημιουργίας που δαγκώνει πιο συχνά. Οι έλεγχοι εμφωλευμένης ύπαρξης πρέπει να βραχυκυκλώνουν, αλλιώς αφήνουν πίσω θέσεις hash.

Hash πινάκων - το κανονικό παράδειγμα#

my @people = (
    { name => 'Alice', dept => 'eng'   },
    { name => 'Bob',   dept => 'sales' },
    { name => 'Carol', dept => 'eng'   },
    { name => 'Dan',   dept => 'sales' },
);

# Group by department:
my %by_dept;
for my $p (@people) {
    push @{ $by_dept{ $p->{dept} } }, $p->{name};
}
# %by_dept = (
#     eng   => ['Alice', 'Carol'],
#     sales => ['Bob', 'Dan'],
# )

# Iterate:
for my $dept (sort keys %by_dept) {
    print "$dept: @{ $by_dept{$dept} }\n";
}
# eng: Alice Carol
# sales: Bob Dan

Η @{ $by_dept{...} } είναι η μορφή αποαναφοράς με αγκύλες. Είναι απαραίτητη επειδή ο στόχος αποαναφοράς είναι μια έκφραση ($by_dept{$dept}), όχι μια σκέτη μεταβλητή.

Η ίδια μορφή με μεταθεματική σύνταξη:

print "$dept: ", join(' ', $by_dept{$dept}->@*), "\n";

Επιλέξτε όποια βρίσκετε πιο σαφή· και οι δύο είναι ιδιωματικές στη σύγχρονη Perl.

Ασθενείς αναφορές και κυκλικές δομές#

Μια αναφορά κρατάει τον αναφερόμενο ζωντανό (με αυξημένο refcount) μέχρι η ίδια η αναφορά να βγει εκτός εμβέλειας. Αυτό γίνεται πρόβλημα όταν δύο δομές δείχνουν η μία στην άλλη:

my $parent = { name => 'p' };
my $child  = { name => 'c', parent => $parent };
push @{ $parent->{children} }, $child;

# parent refers to child via the children arrayref
# child refers back to parent via the parent slot
# When both go out of scope, neither's refcount can reach 0 - leak.

Η λύση είναι μια ασθενής αναφορά - μια αναφορά που δεν αυξάνει το refcount. Η θέση $child->{parent} είναι αυτή που πρέπει να ασθενοποιηθεί (ο πίσω δείκτης):

use Scalar::Util qw(weaken);

my $parent = { name => 'p' };
my $child  = { name => 'c', parent => $parent };
weaken $child->{parent};                 # break the cycle
push @{ $parent->{children} }, $child;

Μετά την ασθενοποίηση, η $child->{parent} διαβάζεται ως ο γονέας όσο υπάρχει, και γίνεται σιωπηλά undef όταν ο γονέας απελευθερωθεί. Η κανονική θέση για weaken είναι σε κάθε πίσω δείκτη σε δενδρική δομή όπου τα παιδιά δείχνουν πίσω στον γονέα τους.

Το κατηγόρημα Scalar::Util::isweak ελέγχει αν μια δεδομένη αναφορά είναι ασθενοποιημένη.

Ισότητα αναφορών#

Δύο αναφορές είναι ίσες (αριθμητικά ==, συμβολοσειριακά eq) αν και μόνο αν αναφέρονται στην ίδια υποκείμενη τιμή:

my @a = (1, 2, 3);
my $r1 = \@a;
my $r2 = \@a;
my $r3 = [1, 2, 3];      # different anonymous array

$r1 == $r2               # TRUE - same address
$r1 == $r3               # FALSE - different array, even though contents match

Η αριθμητική σύγκριση αναφορών συγκρίνει ουσιαστικά τη διεύθυνση μηχανής τους. Δύο αναφορές με το ίδιο περιεχόμενο αλλά διαφορετικές ταυτότητες συγκρίνονται ως άνισες. Η σε βάθος ισότητα χρειάζεται βοηθό από Test::More ή χειροκίνητο περιηγητή.

Μια αναφορά μετατρέπεται σε συμβολοσειρά σαν κάτι σαν ARRAY(0x55ab12cd) - όνομα κλάσης συν δεκαεξαδική διεύθυνση. Μη βασίζεστε στη μορφή· χρησιμοποιήστε την μόνο ως εκτύπωση αποσφαλμάτωσης.

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

  • Arrays, Hashes - οι δύο περιέκτες στους οποίους δείχνουν οι περισσότερες αναφορές.

  • Typeglobs - *name, η εγγραφή πίνακα συμβόλων που οι αναφορές σε globs σας επιτρέπουν να κάνετε ψευδώνυμο.

  • Arrow operator - ->, η συντομογραφία αποαναφοράς.

  • Subscript - [] και {} μέσω αναφοράς.

  • ref - σε τι είδος πράγμα δείχνει αυτή.

  • bless - μετατροπή αναφοράς σε αντικείμενο.

  • Scalar::Util - weaken, isweak, reftype, blessed, looks_like_number.

  • References tutorial - βήμα-προς-βήμα οδηγός-συνοδός αυτής της συνοπτικής αναφοράς.

  • Weak refs tutorial - το πρόβλημα των κύκλων σε βάθος.