Πίνακες πινάκων#
Οι πίνακες της Perl κρατούν βαθμωτά, και τα βαθμωτά δεν μπορούν να περιέχουν άμεσα άλλους πίνακες. Για να κατασκευάσετε έναν δισδιάστατο πίνακα - μήτρα, πλέγμα, πίνακα γραμμών - αποθηκεύετε αναφορές σε εσωτερικούς πίνακες ως στοιχεία του εξωτερικού πίνακα. Αυτό το κεφάλαιο δείχνει πώς.
Κατασκευή μιας μήτρας#
Η πιο άμεση μορφή χρησιμοποιεί ανώνυμους κατασκευαστές πινάκων ([...], καλύπτονται αναλυτικά στις Ανώνυμες αναφορές) ώστε να μη χρειάζεται να ονομάσετε κάθε εσωτερικό πίνακα:
my @m = (
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
);
Ο @m έχει τρία στοιχεία. Κάθε στοιχείο είναι ένα βαθμωτό που κρατάει αναφορά πίνακα. Υπάρχουν τέσσερις πίνακες στο παιχνίδι: ο εξωτερικός @m και τρεις ανώνυμοι εσωτερικοί πίνακες.
Ανάγνωση και εγγραφή στοιχείων#
Το $m[1] είναι η αναφορά στη δεύτερη γραμμή. Για να φτάσετε στο τρίτο στοιχείο αυτής της γραμμής, εφαρμόστε το βέλος:
$m[1]->[2] # 6
$m[0]->[0] # 1
$m[2]->[2] = 99; # overwrite the bottom-right cell
Μεταξύ δύο δεικτών, το βέλος είναι προαιρετικό. Αυτός ο κανόνας είναι που κάνει την πολυδιάστατη πρόσβαση ευανάγνωστη:
$m[1][2] # same as $m[1]->[2]
$m[2][2] = 99; # same as $m[2]->[2] = 99
Το βέλος παραμένει απαραίτητο μεταξύ μιας βαθμωτής μεταβλητής και του πρώτου της δείκτη: το $aref->[0] δεν μπορεί να συντομευθεί σε $aref[0] επειδή αυτό θα σήμαινε «πρώτο στοιχείο του @aref». Η συντόμευση εφαρμόζεται μόνο εντός μιας αλυσίδας δεικτών.
Για τρεις διαστάσεις είναι η ίδια ιδέα:
$cube[2][3][5] # readable
${${$cube[2]}[3]}[5] # the same expression fully spelled out
Η δεύτερη μορφή είναι αυτό που θα έπρεπε να γράψετε χωρίς τη συντόμευση με βέλος. Κανείς δεν το κάνει αυτό.
Επανάληψη πάνω σε μια μήτρα#
Για να διασχίσετε γραμμές, κάντε βρόχο πάνω στον εξωτερικό πίνακα· κάθε επανάληψη σας δίνει μια αναφορά πίνακα. Για να διασχίσετε κελιά, αποαναφέρετε αυτή την αναφορά μέσα στον βρόχο:
for my $row (@m) {
for my $cell (@{$row}) {
print "$cell ";
}
print "\n";
}
Αν χρειάζεστε επίσης τους δείκτες, χρησιμοποιήστε αριθμημένο βρόχο πάνω στον εξωτερικό πίνακα και στον αποαναφερόμενο εσωτερικό:
for my $i (0 .. $#m) {
for my $j (0 .. $#{$m[$i]}) {
print "m[$i][$j] = $m[$i][$j]\n";
}
}
Το $#{$m[$i]} είναι ο τελεστής τελευταίου δείκτη εφαρμοσμένος στον αποαναφερόμενο εσωτερικό πίνακα. Είναι η μορφή $#array με το {$m[$i]} να αντικαθιστά το όνομα του πίνακα.
Επέκταση γραμμών και στηλών#
Προσαρτήστε σε μια γραμμή κάνοντας push στον εσωτερικό πίνακα μέσω αναφοράς:
push @{$m[0]}, 10; # row 0 is now (1, 2, 3, 10)
Προσαρτήστε ολόκληρη γραμμή στη μήτρα κάνοντας push μιας αναφοράς πίνακα στον εξωτερικό πίνακα:
push @m, [10, 11, 12]; # @m now has four rows
Και οι δύο λειτουργίες χρησιμοποιούν την push - μία φορά σε αποαναφερόμενο εσωτερικό πίνακα, μία φορά απευθείας στον εξωτερικό πίνακα.
Αυτο-δημιουργία#
Η ανάθεση στο $m[$i][$j] λειτουργεί ακόμα και αν το $m[$i] δεν υπάρχει ακόμα:
my @grid;
$grid[5][7] = 'X';
Αυτό δημιουργεί τον @grid με έξι κενές θυρίδες και μία αναφορά στη θυρίδα 5. Αυτή η αναφορά δείχνει σε έναν φρέσκο ανώνυμο πίνακα του οποίου η όγδοη θυρίδα κρατάει 'X'. Η Perl δημιουργεί αυτόματα και την εξωτερική θυρίδα και τον εσωτερικό πίνακα. Αυτό λέγεται αυτο-δημιουργία και είναι ο λόγος που σχεδόν ποτέ δεν χρειάζεται να γράψετε $m[$i] = [] πριν τη χρήση.
Η αυτο-δημιουργία ενεργοποιείται κατά την ανάθεση και κατά την αποαναφορά σε περιβάλλον lvalue. Οι σκέτες αναγνώσεις δεν αυτο-δημιουργούν:
my @grid;
my $x = $grid[5][7]; # $x is undef; no inner array created
exists $grid[5]; # false
Χρησιμοποιήστε την defined για να ξεχωρίσετε την περίπτωση «δεν υπήρξε ποτέ» από την περίπτωση «υπάρχει και κρατάει undef», και την exists για να ελέγξετε την ίδια τη θυρίδα χωρίς να την αυτο-δημιουργήσετε.
Συνηθισμένο λάθος: κοινή χρήση ενός μοναδικού εσωτερικού πίνακα#
Αυτό μοιάζει με τρεις γραμμές αλλά είναι στην πραγματικότητα μία γραμμή επαναλαμβανόμενη:
my @inner = (0, 0, 0);
my @bad = (\@inner, \@inner, \@inner);
$bad[0][1] = 9;
print "$bad[2][1]\n"; # 9 - they're all the same array
Για να πάρετε τρεις ανεξάρτητες γραμμές, κατασκευάστε καθεμία ξεχωριστά. Η μορφή ανώνυμου κατασκευαστή το χειρίζεται κατανέμοντας πάντα φρέσκα:
my @good = ([0, 0, 0], [0, 0, 0], [0, 0, 0]);
$good[0][1] = 9;
print "$good[2][1]\n"; # 0
Ένας βρόχος μέσω map δίνει το ίδιο αποτέλεσμα για γραμμές μεταβλητού μήκους:
my @grid = map { [ (0) x 10 ] } 1 .. 10; # 10×10 of zeros
Συνηθισμένο λάθος: αποθήκευση του πλήθους αντί των στοιχείων#
Η ανάθεση ενός πίνακα σε μια μεμονωμένη βαθμωτή θυρίδα αποτιμά τον πίνακα σε βαθμωτό περιβάλλον, που δίνει το πλήθος των στοιχείων του, όχι το περιεχόμενό του:
for my $i (1 .. 10) {
my @array = somefunc($i);
$AoA[$i] = @array; # WRONG - stores the count
}
Μετά από αυτόν τον βρόχο κάθε $AoA[$i] είναι ένας αριθμός. Για να αποθηκεύσετε τα στοιχεία, τυλίξτε τα σε έναν κατασκευαστή ανώνυμου πίνακα ώστε να πάρετε μια αναφορά σε ένα φρέσκο αντίγραφο:
$AoA[$i] = [ @array ]; # right - a new array, copied
Αν πραγματικά θέλατε το πλήθος, πείτε το με scalar και ένα όνομα που το αντικατοπτρίζει:
$counts[$i] = scalar @array; # explicit - no surprise later
Συνηθισμένο λάθος: ένας επαναχρησιμοποιημένος πίνακας πίσω από κάθε αναφορά#
Η παγίδα του επαναλαμβανόμενου πίνακα από την προηγούμενη ενότητα εμφανίζεται επίσης όταν ο κοινόχρηστος πίνακας επαναχρησιμοποιείται μεταξύ επαναλήψεων του βρόχου:
our @array;
for my $i (1 .. 10) {
@array = somefunc($i);
$AoA[$i] = \@array; # WRONG - same array every time
}
Κάθε \@array είναι μια αναφορά στον ίδιο πίνακα, οπότε και οι δέκα θυρίδες καταλήγουν να δείχνουν σε ό,τι επέστρεψε τελευταίο η somefunc. Και πάλι, το [ @array ] το διορθώνει μέσω αντιγραφής.
Η δήλωση του πίνακα με my μέσα στον βρόχο είναι ασφαλής, ωστόσο:
for my $i (1 .. 10) {
my @array = somefunc($i);
$AoA[$i] = \@array; # fine - fresh array each iteration
}
Το my @array χτίζει έναν νέο πίνακα σε κάθε πέρασμα, οπότε κάθε \@array αναφέρεται σε έναν ξεχωριστό. Η αναφορά επιβιώνει του σώματος του βρόχου επειδή ο πίνακας στον οποίο δείχνει παραμένει ζωντανός όσο παραμένει και η αναφορά. Η μορφή κατασκευαστή [ @array ] είναι ακόμη η σαφέστερη επιλογή· καταφύγετε στη μορφή \@array μόνο όταν καταλαβαίνετε γιατί λειτουργεί εδώ.
Πού να πάτε στη συνέχεια#
Κατακερματισμοί και μείξεις - κατακερματισμοί πινάκων και πίνακες κατακερματισμών, τα δύο σχήματα που καλύπτουν τις περισσότερες εγγραφές του πραγματικού κόσμου.
Ανώνυμες αναφορές - οι κατασκευαστές
[...]/{...}που χρησιμοποιήθηκαν παραπάνω, αναλυτικά.