Μια από τις συνηθέστερες εργασίες μας είναι η διαγραφή όλων των διπλοτύπων από μια λίστα. Για παράδειγμα, έχω μια λίστα με URLs πολλά από τα οποία είναι καταγεγραμμένα εκεί δύο ή περισσότερες φορές και θέλω να την «καθαρίσω» έτσι ώστε να περιέχει κάθε URL μόνο μια φορά. Για να γίνει αυτό θα χρησιμοποιήσω την grep:
my %exist;
Πρώτα δημιουργώ μια hash μέσω της οποία θα «καθαρίσω» όλες τις διπλότυπες τιμές.
my @uniqueURL = grep {! $exist{$_}++} @allURLs;
Αυτή η μαγική γραμμή κώδικα θα κάνει όλη τη δουλειά. Όπως συμβαίνει πολλές φορές στην Perl για να την καταλάβουμε πρέπει να διαβάσουμε από δεξιά προς τα αριστερά. Ο κώδικάς μας λέει: «Πάρε την array @allURLs (όλες οι τιμές της είναι URLs). Η $exist αποτελεί το key της %exist. Κάθε φορά που συναντάς μια τιμή της @allURLs (που η Perl τις περνάει όλες μία μία από την $_) πρόσθεσε μια μονάδα στην value που έχει για key αυτό το URL.
Εξυπακούεται (η Perl μερικές φορές είναι πολύ έξυπνη) ότι αν δεν υπάρχει αυτό το key στην %exist, τότε θα δημιουργηθεί με τιμή 1. Με τον τρόπο αυτό λοιπόν η %exist περιέχει πλέον ως keys όλα τα URLs της @allURLs, ενώ το value κάθε URL είναι ο αριθμός των επαναλήψεών του.
Πηγαίνοντας πιο αριστερά βλέπουμε και την grep. Αυτό που λέμε στην Perl είναι πως πρέπει να παρακολουθεί όλα όσα συμβαίνουν μέσα στα άγκιστρα που ακολουθούν και αν δεν υπάρχει (αυτό υποδηλώνει το θαυμαστικό) η $exist ($_), δηλαδή αν δεν υπάρχει κάποιο URL που να βρίσκεται ήδη μέσα στην %exist, τότε αυτό το URL να καταχωρείται στην @uniqueURL.
Γενικά η grep αποτελεί ένα φίλτρο που ξεχωρίζει με βάση κάποιους κανόνες τα περιεχόμενα μιας λίστας. (Στην προκειμένη περίπτωση της @allURLs.)
Στο παραπάνω παράδειγμα είχαμε την ακόλουθη γενική σύνταξη:
grep - {μια σειρά από εργασίες} - λίστα (η @allURLs)
Μια πιο απλή σύνταξη θα ήταν η:
grep μια έκφραση - λίστα
Για παράδειγμα:
my @beginxlist = grep m/^x/, @biglist;
Εδώ η @beginxlist αποτελείται από όσες τιμές της @biglist ξεκινούν από x. (Προσέξτε το κόμμα πριν την @biglist που δεν υπήρχε στο πιο περίπλοκο παράδειγμα με τις αγκύλες).
Sort
Η Perl διαθέτει μερικές βασικές διαδικασίες ταξινόμησης. Για παράδειγμα, αν:
@numberlist = (15, 7, 28, 6)
τότε το:
@numberlist = sort { $a <=> $b } @numberlist;
μας δίνει: @numberlist = (6, 7, 15, 28)
Αν δηλώναμε { $b <=> $a } τότε θα είχαμε:
@numberlist = (28, 15, 7, 6), δηλαδή αντίστροφη ταξινόμηση.
Ο comparison operator (<=>) δεν χρησιμοποιείται κατά την ταξινόμηση strings. Για παράδειγμα:
my @my_array = ('a', 'd', 'y', 'b', 'c');
my @ordered_array = sort @my_array;
print "\@my_array \= @my_array\n";
Μας δίνει: @my_array = a d y b c
print "\@ordered_array \= @ordered_array\n";
Μας δίνει: @ordered_array = a b c d y
Όπως παρατηρείτε η @my_array δεν μεταβλήθηκε καθόλου.
Πιο περίπλοκο είναι το ακόλουθο παράδειγμα:
foreach my $key (sort{$exist{$b} <=> $exist{$a}} keys %exist)
{
print "$key $exist{$key}\n";
}
Όπως ίσως θα θυμάστε από το παράδειγμα της grep, η %exist περιέχει μια λίστα με μοναδικά URLs, καθώς και τις φορές που υπήρχε το καθένα σε μια άλλη λίστα (που την πήραμε από ένα log file). Στις hash όμως τα δεδομένα τοποθετούνται σε τυχαία σειρά και εμείς θέλουμε να ξέρουμε πόσα URLs επαναλήφθηκαν τις περισσότερες φορές. Γι αυτό, θα τα ταξινομήσουμε ανάλογα με την value τους (κάθε URL αποτελεί και ένα ξεχωριστό Key).
Για να μην ξεχνιόμαστε υπενθυμίζω ότι η value ενός key μιας hash απεικονίζεται ως:
$hashname{key}
Έτσι λοιπόν, παραπάνω κώδικας λέει στην Perl:
«Σορτάρησε» ένα ένα (foreach) τα keys της %exist με βάση τις values αντίστροφα (από τη μεγαλύτερη προς τη μικρότερη) και τύπωσε ένα ένα τα keys μαζί με την τιμή τους.
Πιο απλό φυσικά θα ήταν να «σορτάρουμε» μόνο τα keys.
my %hash_test = ( 'giorgos' => 'brother', 'sophia' => 'sister', 'stefanos' => 'father', 'ioanna' => 'mother');
my @sorted_keys = sort {$b <=> $a} keys %hash_test;
print "@sorted_keys\n";
Εδώ η εκτύπωση θα μας δώσει: giorgos ioanna sophia stefanos Δημιουργήσαμε δηλαδή μια νέα array (την @sorted_keys) που περιέχει όλα τα keys σε αλφαβητική σειρά.
Αν πάντως τρέξατε τον παραπάνω κώδικα θα παρατηρήσατε το μήνυμα λάθους:
Argument "sophia" isn't numeric in sort at test2.pl line 4.
Argument "stefanos" isn't numeric in sort at test2.pl line 4
Argument "ioanna" isn't numeric in sort at test2.pl line 4.
Argument "giorgos" isn't numeric in sort at test2.pl line 4.
Αυτό συμβαίνει διότι στη sort χρησιμοποιήθηκε ο τελεστής <=> αντί για τον cmp. Ο πρώτος χρησιμοποιείται για συγκρίς εις αριθμών, ενώ ο δεύτερος για strings (ακολουθίες χαρακτήρων). Ο σωστός κώδικας λοιπόν θα είναι:
my %hash_test = ( 'giorgos' => 'brother', 'sophia' => 'sister', 'stefanos' => 'father', 'ioanna' => 'mother');
my @sorted_keys = sort {$b cmp $a} keys %hash_test;
print "@sorted_keys\n";