Μεταβλητές – hashes
Οι μεταβλητές κατακερματισμένων πινάκων, στις οποίες αναφερόμαστε με το όνομα hashes (αγγλικά για το 'κατακερματισμός', 'σύνολο θρυμμάτων') είναι μη διατεταγμένοι πίνακες που χρησιμοποιούνται για την αναπαράσταση πολυδιάστατων δεδομένων. Η βασική τους χρήση είναι η σύνδεση δύο σειρών δεδομένων (arrays) μέσω μιας ορισμένης σχέσης ανά ζεύγος, με τον ίδιο τρόπο που μπορούμε να φανταστούμε ένα σύστημα ονομάτων τηλεφωνικού καταλόγου.
Tα hashes έχουν μια χαρακτηριστική δομή ζευγαρωτού πίνακα, καθένας από τους οποίους υπόκειται στους βασικούς περιορισμούς των πινάκων. Μπορούν να είναι δηλαδή ζεύγη πινάκων αριθμών (ακέραιων ή δεκαδικών), σειρών χαρακτήρων, ή αλφαριθμητικών στοιχείων και συνδυασμοί αυτών. Η βασική διαφορά των hashes σε σχέση με τις βαθμωτές μεταβλητές και τους πίνακες είναι η διάταξή τους.
Τα hashes συμβολίζονται με το σύμβολο % (με τρόπο αντίστοιχο αυτού με τον οποίον οι πίνακες συμβολίζονται με το @ και οι βαθμωτές μεταβλητές με το $). Για να δηλώσουμε ένα hash υπάρχουν πολλοί τρόποι. Για μικρά hashes μπορούμε απλώς να δηλώσουμε τα ζεύγη των τιμών σε στήλες μέσα σε μια παρένθεση και χωρίζοντάς τα με το σύμβολο '=>'.
#!/usr/bin/perl #use warnings; %hash = ( 'Me' => 'one', 'You' => 'two', 'Him' => 'three', );
ή απλά διαχωρισμένα με κόμματα:
#!/usr/bin/perl #use warnings; %hash = ( 'Me' , 'one', 'You' , 'two', 'Him' , 'three', );
Οι δύο πιο πάνω δομές είναι ισοδύναμες. Στην ουσία υπάρχει μια εντολή που είναι εντολή εκχώρησης μεταβλητών. Η μεταβλητή είναι η %hash η εκχώρηση γίνεται ως %hash=(). To σημαντικό για τη δομή είναι η σειρά των αντικειμένων στις δύο στήλες που ακολουθούν. Τα στοιχεία στην αριστερή στήλη, ή στην περίπτωση χρήσης του
Κάθε στοιχείο έχει μια εσωτερική τιμή κλειδί (key) μέσα στο hash. Αυτές αποδίδονται από τον χρήστη, είναι μοναδικές και η διάταξή τους καθορίζεται αυτόματα από τον υπολογιστή με τυχαία σειρά που εμείς δεν ελέγχουμε. Η εξωτερική τιμή (value) είναι αυτή που αποδίδουμε εμείς στο στοιχείο.
Τα δύο χαρακτηριστικά των keys είναι ιδιαίτερα σημαντικά:
#!/usr/bin/perl #use warnings; %hash = ( 'Μe' => 'one', 'You' => 'two', 'Him' => 'three', ); print %hash,"\n";
Αν εκτελέσετε το παραπάνω πρόγραμμα πολλές φορές θα δείτε πως το αποτέλεσμα διαφέρει. Η σειρά με την οποία εμφανίζονται οι τιμές δεν είναι ίδια. Τα κλειδιά βέβαια είναι ακόμα συνδεδεμένα με τις τιμές, αλλά η σειρά είναι τυχαία. Επίσης η εκτύπωση αποδίδει όλα τα δεδομένα σε μια σειρά αλλά αυτό είναι κάτι που θα δούμε πώς λύνεται στη συνέχεια.
#!/usr/bin/perl #use warnings; %hash = ( 'Me' => 'one', 'You' => 'two', 'Him' => 'three', 'Me' => 'four', );
print %hash,"\n";
Όσες φορές κι αν το εκτελέσετε η τιμή που θα δείτε δίπλα στο κλειδί 'Me' θα είναι το 'four'. Αυτό γιατί λόγω της μοναδικότητας των κλειδιών μόνο η τελευταία σχέση καταγράφεται στη μνήμη. Με τον ίδιο τρόπο που μια βαθμωτή μεταβλητή θα κρατήσει την τελευταία τιμή που της αποδώθηκε, έτσι και το κάθε κλειδί σε ένα hash θα κρατήσει την τελευταία τιμή value που του αντιστοιχήθηκε. Για την ακρίβεια όπως θα δούμε και παρακάτω κάθε κλειδί (key) και κάθε τιμή (value) είναι βαθμωτές μεταβλητές.
Κάθε κλειδί του hash καθώς και κάθε τιμή του hash ειναι βαθμωτές μεταβλητές. Έτσι το %hash το στοιχείο με key='Μe' έχει την τιμή 'one'. Η χρήση της βαθμωτής τους ιδιότητας γίνεται μέσω της επίκλησης ενός στοιχείου στο hash ως βαθμωτής μεταβλητής:
#!/usr/bin/perl #use warnings; %hash = ( 'Me' => 'one', 'You' => 'two', 'Him' => 'three', 'Me' => 'four', ); print $hash{'Me'},"\n";
Η σειρά των παραπάνω εντολών τυπώνει μόνο τη λέξη "four" που είναι η αντίστοιχη για το κλειδί Me. Προσέξτε ότι η επίκληση της εντολής print αναφέρεται τώρα σε βαθμωτή μεταβλητή $hash{'Me'} καθώς ουσιαστικά ζητάμε από το πρόγραμμα να μας τυπώσει την τιμή που έχει το hash για το κλειδί 'Me' που είναι όπως είπαμε μια βαθμωτή μεταβλητή.
Η ασυμμετρία που αναφέρθηκε παραπάνω μεταξύ κλειδιών και τιμών πρέπει να γίνει σαφής και εδώ. Μπορούμε να ανακαλέσουμε την τιμή για ένα κλειδί αλλά όχι το κλειδί για μια τιμή, καθώς μια τιμή μπορεί εξ ορισμού να αντιστοιχεί σε πολλά κλειδιά.
Άλλες λειτουργίες πάνω στα κλειδιά μπορεί να είναι:
Απόδοση νέας τιμής σε κλειδί με απλή απόδωση βαθμωτής μεταβλητής:
$hash{'Me'}='five';
Η πιο πάνω εντολή μπορεί να εκτελεστεί και χωρίς τα εισαγωγικά εφόσον το κλειδί είναι μια λέξη:
$hash{Me}='five';
η οποία μπορεί να γίνει και για ένα τελείως νέο ζεύγος κλειδιών-τιμής (προσοχή να μην έχει επαναληφθεί το κλειδί)
$hash{'Ηer'}='ten';
Διαγραφή στοιχείου
delete $hash{'Her'};
Από τα παραπάνω θα πρέπει να έχουν γίνει σαφή τα παρακάτω:
print $hash{$key},"\n";
H παραπάνω εντολή τυπώνει στην οθόνη την τιμή του hash για το κλειδί $key.
Τα κλειδιά keys και οι τιμές values ενός hash είναι πίνακες (arrays). Έτσι μπορούμε να τις χειριστούμε ως τέτοιους στο βαθμό που αυτό μας επιτρέπεται από τη δομή του hash. H εντολή:
@mykeys=keys(%hash);
αποδίδει τα κλειδιά ενός hash σε έναν πίνακα (τύπου array).
με τον ίδιο τρόπο ή values αποδίδει τις τιμές ενός hash σε έναν πίνακα:
@myvalues=values(%hash);
Η χρήση της δεύτερης είναι σπάνια, ωστόσο η πρώτη χρησιμοποιείται κατά κόρον σε όλες τις επαναληπτικές διαδικασίες με τις οποίες χειριζόμαστε ένα hash. Αξίζει να σημειώσουμε εδώ ότι και κατά την εκτέλεση των keys και values η σειρά με την οποία επιστρέφονται οι τιμές είναι τυχαία (ή για την ακρίβεια δεν είναι αυτή που περιμένουμε). Για το λόγο αυτό χρειάζεται προσοχή στον χειρισμό κάθε hash.
keys, values
Tις έχουμε ήδη δει παραπάνω. Xρησιμοποιούνται για την προσπέλαση των hashes κυρίως η keys με σκοπό την ανάγνωση, μετατροπή και εκτύπωσή τους μέσω επαναληπτικών διαδικασιών πάνω σε πίνακες.
Μέγεθος hash
Γίνεται έμμεσα με την απαρίθμηση του μεγέθους του πίνακα των κλειδιών ενός hash
$sizeofhash=scalar(keys(%hash))
Κατάταξη hash
Tα hashes είναι εξ ορισμού μη διατεταγμένοι πίνακες και δεν μπορούν να χρησιμοποιηθούν ως ορίσματα συναρτήσεων τύπου sort. Το sort ωστόσο μπορεί να χρησιμοποιηθεί για να κατατάξει με αλφαριθμητική σειρά τον πίνακα κλειδιών keys ενός hash και ως εκ τούτου μπορούμε να κατατάξουμε έναν hash με τη σειρά των κλειδιών του.
@keysofhash=keys(%hash);
@sortedkeys=sort{ $a <=> $b}@keysofhash;
Έχοντας καταταγμένα τα κλειδιά του hash μπορούμε να προσπελάσουμε τα στοιχεία του στη σειρά, όπως θα περιγράψουμε παρακάτω.
reverse
Η reverse, aντιστρέφει την ΔΟΜΗ των στοιχείων του hash. Τα κλειδιά είναι τώρα τιμές και οι τιμές κλειδιά.
%newhash=reverse(%hash);
Θα πρέπει να θυμόμαστε πάντως ότι δεδομένου ότι τα κλειδιά είναι μοναδικά αλλά όχι οι τιμές, η αντιστροφή αυτή ενδέχεται να αλλάξει τις σχέσεις μεταξύ τους. Για την ακρίβεια ο hash μπορεί να μικρύνει καθώς αν υπήρχαν πολλές εμφανίσεις της ίδιας τιμής τότε αυτή η τιμή θα εμφανίζεται στον νέο hash ως μοναδικό κλειδί και η τιμή που θα αντιστοιχεί σε αυτό θα είναι το τελευταίο κλειδί που αναγνώστηκε.
exists - defined
Οι συναρτήσεις exist, defined επιτελούν τον έλεγχο ύπαρξης ενός στοιχείου. Είναι πολύ σημαντικές μιας και τα στοιχεία δεν είναι διατεταγμένα κι εμείς πολλές φορές δεν γνωρίζουμε αν μια τιμή έχει εκχωρηθεί ή όχι. Συντάσσονται σχεδόν αποκλειστικά μέσα σε δομές ελέγχου.
if exists $hash{1} {…} (→ αληθές αν η τιμή υπάρχει)
if defined $hash{1} {…} (→ αληθές αν η τιμή έχει δηλωθεί κάποια στιγμή ακόμα κι αν δεν υπάρχει πλέον)
delete
Διαγράφει (προφανώς) ένα στοιχείο. ΔΗΛΑΔΗ ένα ζεύγος κλειδιού-τιμής. Το όρισμά της είναι η βαθμωτή κλειδιού ΑΛΛΑ αυτό που διαγράφεται είναι το ζεύγος κλειδιού-τιμής.
delete $hash{$a};
foreach
Οι επαναληπτικές διαδικασίες είναι ο μοναδικός τρόπος να διατρέξει κανείς ένα hash στο σύνολό του, δεδομένου ότι πρόκειται για μη διατεταγμένες δομές δεδομένων. Για παράδειγμα αν θέλουμε να τυπώσουμε ένα hash στοιχείο προς στοιχείο σε δύο στήλες με τα κλειδιά στην πρώτη και τις αντίστοιχες τιμές στη δεύτερη, μπορούμε να χρησιμοποιήσουμε μια συνάρτηση foreach που θα επαναλαμβάνεται πάνω στον πίνακα των κλειδιών του:
foreach $key (keys (%hash)) {
print $key, "\t", $hash{$key},"n";
}
η επανάληψη foreach χρειάζεται την συνάρτηση keys
Εκτελείται έτσι μια επανάληψη για κάθε κλειδί του hash.
for
Κάτι απολύτως αντίστοιχο μπορεί να γίνει με μια εντολή for
for $key (keys %hash) {
$hash{$key}+=1;
}
Oι παραπάνω εντολές θα αυξήσουν όλες τις τιμές του hash κατά 1 για όλα τα κλειδιά.
each
Υπονοεί έναν βρόχο επανάληψης εντός του οποίου η διαδικασία επαναλαμβάνεται για όλα τα στοιχεία του hash. Χρησιμοποιείται κυρίως σε βρόχους.
while ( ($no, $number) = each(%hash) ) {…
Τα $no και $number είναι τα ζεύγη κλειδιου-τιμής
}
Κατεβάσετε το αρχείο genetic_code.txt και να το χρησιμοποιήσετε σε ένα hash με το οποίο θα κάνετε τα παρακάτω:
1. Θα ανοίξετε το human.fa από το προηγούμενο κεφάλαιο και θα το διαβάσετε σε ένα string
2. Θα μεταφράσετε το human.fa σε πρωτεϊνική αλληλουχία, διαβάζοντας το ανα τρια νουκλεοτίδια.