Το προφίλ μας στο Google Plus
0

Ανάπτυξη εφαρμογών σε C# για το .NET [μέρος 6ο]

Σε αυτό το μέρος της σειράς μας κατακτάμε τις απαραίτητες γνώσεις ώστε τα προγράμματά μας να χειρίζονται τα διάφορα δεδομένα με τον πιο αποδοτικό τρόπο. Σε αυτή την υπόθεση εξυπηρετούν οι δομές δεδομένων, οι οποίες δεν οργανώνουν μόνο δεδομένα, αλλά και την ίδια τη σκέψη ενός προγραμματιστή.

Η αλληλεπίδραση με το σύστημα αρχείων, με την οποία εξοικειωθήκαμε στο προηγούμενο τεύχος, αποτελεί μια κρίσιμη πτυχή του προγραμματισμού σε οποιαδήποτε γλώσσα. Ωστόσο, πριν φτάσουμε να αποθηκεύσουμε οτιδήποτε σε κάποιο αρχείο, όπως και πριν ανοίξουμε κάποιο για ανάγνωση, πρέπει να έχουμε στήσει έναν μηχανισμό που θα επεξεργάζεται τα δεδομένα μας. Ο πιο πολύτιμος σύμμαχος σε αυτή την εργασία είναι οι δομές δεδομένων, που επιτρέπουν στον προγραμματιστή να οργανώνει και να διαχειρίζεται τα διάφορα δεδομένα με ευκολία. Στο παρόν τεύχος ασχολούμαστε με τις δομές δεδομένων που προσφέρει το .NET framework.

Συλλογές
Μια δομή δεδομένων που έχουμε ήδη χρησιμοποιήσει είναι οι πίνακες. Οι πίνακες (arrays) στη C#, όπως και σε αρκετές ακόμα γλώσσες, αποτελούν μια στατική δομή δεδομένων. (Αυτό ισχύει στις λεγόμενες στατικές γλώσσες. Παρεμπιπτόντως, η C# δεν είναι αμιγώς στατική, αφού παρέχει και αρκετούς δυναμικούς τύπους δεδομένων.) Άπαξ και δημιουργηθεί ένας πίνακας, δεν μπορεί να αλλάξει το μέγεθός του. Για να προσθέσουμε στοιχεία, το μόνο που μπορούμε να κάνουμε είναι να δημιουργήσουμε ένα μεγαλύτερο πίνακα, να αντιγράψουμε σ’ αυτόν τα περιεχόμενα του παλιού και να προσθέσουμε τα νέα δεδομένα. Ίσως αυτό να ακούγεται αρκετά περιοριστικό και σχεδόν δυσάρεστο, αλλά δεν είναι. Αφενός, τις περισσότερες φορές γνωρίζουμε εξ’ αρχής πόσα στοιχεία θα φιλοξενεί ο εκάστοτε πίνακας κι αφετέρου, στην περίπτωση που χρειαζόμαστε έναν πιο ελαστικό αποθηκευτικό χώρο, μπορούμε να στραφούμε σε κάποια από τις δυναμικές δομές δεδομένων που ενσωματώνει το .ΝΕΤ framework. Μια τέτοια δομή είναι η ArrayList. Ουσιαστικά πρόκειται για πίνακες, το μήκος των οποίων μπορεί να μεταβάλλεται δυναμικά. Οι πίνακες αυτού του είδους αποθηκεύουν τα δεδομένα ως αντικείμενα μιας προκαθορισμένης κλάσης και η σχετική μετατροπή πραγματοποιείται αυτόματα. Όταν θέλουμε να διαβάσουμε δεδομένα που έχουν αποθηκευτεί στον πίνακα, πρέπει να γνωρίζουμε τον τύπο τους και να πραγματοποιούμε τις αντίστροφες μετατροπές χειροκίνητα. Αν αυτή η περιγραφή σας μπέρδεψε, μελετήστε το ακόλουθο παράδειγμα. Πρόκειται για ένα πρόγραμμα που ζητά από το χρήστη να πληκτρολογήσει τους επιθυμητούς προορισμούς για τις καλοκαιρινές διακοπές, μετά τους αποθηκεύει σε ένα ArrayList και στη συνέχεια τους ανακαλεί για να τους τυπώσει στην κονσόλα.

using System;
using System.Collections;

namespace ArrayListExample{
   class Program{
      public static void Main(string[] args){
         ArrayList dest_al = new ArrayList();
         string destination;
         int i = 0;
         
         Console.Title = "dh044 - C# Μέρος 6ο - Παράδειγμα ArrayList";
         Console.WriteLine("Εισάγετε τα ονόματα των προορισμών σας.\nΔώστε κενή συμβολοσειρά για τερματισμό.");
         do {
            Console.Write("Προορισμός " + (++i).ToString() + " : ");
            destination = Console.ReadLine();
            dest_al.Add(destination);
            } while (destination != "");

         dest_al.RemoveAt(dest_al.Count - 1);
         
         Console.WriteLine("\n\nΦέτος θα πάτε:");
         foreach(object element in dest_al){
            Console.WriteLine(" - " + (string)element);
         }
         Console.ReadKey(true);
      }
   }
}

Το πρώτο πράγμα που πρέπει να κάνουμε για να εργαστούμε με μια δομή ArrayList, είναι να δηλώσουμε τη χρήση του namespace “System.Collections”. Στη συνέχεια, για τη δημιουργία ενός αντικειμένου ArrayList αρκεί να χρησιμοποιήσουμε τη σχετική συνάρτηση (μέθοδο) δημιουργίας. Όπως βλέπετε στη γραμμή 07, εμείς καλούμε την εν λόγω συνάρτηση χωρίς παραμέτρους. Έτσι, καταφέρνουμε να δημιουργήσουμε ένα νέο αντικείμενο ArrayList, με την προεπιλεγμένη αρχική χωρητικότητα. Θα μπορούσαμε να καθορίσουμε το αρχικό μέγεθος, δίνοντάς το ως παράμετρο στη συνάρτηση κατασκευής. Εναλλακτικά, θα μπορούσαμε να φτιάξουμε ένα ArrayList με αρχικό μέγεθος και περιεχόμενα, που θα προέρχονταν από έναν ήδη υπάρχοντα πίνακα. Σ’ αυτή την περίπτωση θα δίναμε ως παράμετρο στη συνάρτηση κατασκευής τον πίνακα με τα δεδομένα. Στο βρόχο που ακολουθεί (γραμμές 13 έως 17) συλλέγουμε τους προορισμούς διακοπών που δηλώνει ο χρήστης και τους προσθέτουμε στο ArrayList, χρησιμοποιώντας τη μέθοδο Add(object). Η συλλογή των προορισμών σταματάει όταν ο χρήστης εισάγει μια κενή συμβολοσειρά. Επειδή όμως ο έλεγχος τερματισμού βρίσκεται στο τέλος του βρόχου, το ArrayList θα περιλαμβάνει πάντα ως τελευταίο στοιχείο μια κενή συμβολοσειρά. Αυτό δεν είναι επιθυμητό, οπότε χρησιμοποιούμε τη μέθοδο RemoveAt(position) για να αφαιρέσουμε το συγκεκριμένο στοιχείο (γραμμή 19). Για να βρούμε ποια είναι η θέση αυτού του πεδίου –δηλαδή το τελευταίο στοιχείο–, αξιοποιούμε την ιδιότητα Count (επιστρέφει το πλήθος των στοιχείων). Από αυτό το μέγεθος αφαιρούμε μια μονάδα, καθώς η αρίθμηση των στοιχείων ενός ArrayList ξεκινά από το μηδέν. Εναλλακτικά, για τη διαγραφή θα μπορούσαμε να είχαμε χρησιμοποιήσει τη μέθοδο Remove(object). Αυτή η μέθοδος διαγράφει το αντικείμενο που της δίνουμε σαν παράμετρο. Έτσι, μια εναλλακτική μορφή για τη γραμμή 19 είναι η ακόλουθη:

al.RemoveAt("");

Τα στοιχεία ενός ArrayList μπορούν να προσπελαστούν σειραϊκά, με τη χρήση της δομής επανάληψης foreach. Ακριβώς αυτήν χρησιμοποιούμε στο παράδειγμα, για να διαβάσουμε και να εμφανίσουμε τους προορισμούς που έδωσε ο χρήστης. Παρατηρείστε το type casting που πραγματοποιούμε πριν από την εμφάνιση κάθε στοιχείου στην κονσόλα. Αυτή η μετατροπή θα μπορούσε να αυτοματοποιηθεί, ορίζοντας τον τύπο string εντός της δομής foreach. Σε κάθε περίπτωση, κάθε φορά που αντλούμε δεδομένα από ένα ArrayList πρέπει να γνωρίζουμε τον τύπο τους.

Ιδού το island hopping που θέλαμε να κάνουμε φέτος! Εσείς που θα θέλατε να πάτε;

Άλλες χρήσιμες μέθοδοι της κλάσης ArrayList είναι οι ακόλουθες:

  • AddRange(array): εισάγει τα στοιχεία ενός ολόκληρου πίνακα
  • Clear(): διαγράφει όλα τα στοιχεία της λίστας
  • Contains(object): ελέγχει αν υπάρχει το συγκεκριμένο στοιχείο στη λίστα
  • Reverse():αντιστρέφει τη σειρά των στοιχείων της λίστας
  • Sort(): πραγματοποιεί ταξινόμηση των στοιχείων
  • ToArray(): επιστρέφει έναν πίνακα με τα στοιχεία της λίστας

Η κλάση ArrayList αποτελεί ένα πολύ ισχυρό εργαλείο. Ωστόσο, το γεγονός ότι πρέπει να γνωρίζουμε τον τύπο των αποθηκευμένων δεδομένων συνιστά μεγάλο μπελά. Από πολύ νωρίς, η C# άρχισε να υποστηρίζει ένα νέο είδος κλάσεων, που έχει το όνομα generics. Σε αυτό το είδος συγκαταλέγεται και η κλάση List, που καθιστά την ArrayList ξεπερασμένη. Για να καταλάβετε που κολλάει το “generics”, σκεφτείτε ότι οι κλάσεις αυτού του είδους δεν χειρίζονται συγκεκριμένους τύπους δεδομένων. Φυσικά, κάθε κλάση έχει το δικό της τρόπο λειτουργίας, που εκδηλώνεται μέσα από τις αντίστοιχες μεθόδους. Ωστόσο, η φύση των δεδομένων που θα φιλοξενεί καθορίζεται από το χρήστη. Για παράδειγμα, δείτε τη δημιουργία ενός αντικειμένου List, που προορίζεται για το χειρισμό συμβολοσειρών:

List<string> myList = new List<string>();

Οι μέθοδοι και οι ιδιότητες που συναντήσαμε στην κλάση ArrayList απαντώνται και στην κλάση List, ενώ υπάρχουν και αρκετές πρόσθετες. Δείτε πώς θα μπορούσε να γραφεί το πρόγραμμα που εξετάσαμε νωρίτερα, με τη χρήση της κλάσης List:

using System;
using System.Collections.Generic;

namespace ArrayListExample{
  class Program{
     public static void Main(string[] args){
        List<string> dest_lst = new List<string>();
        string destination;
        int i = 0;
        
        Console.Title = "dh044 - C# Μέρος 6ο - Παράδειγμα Generics List";
        Console.WriteLine("Εισάγετε τα ονόματα των προορισμών σας.\nΔώστε κενή συμβολοσειρά για τερματισμό.");
        do{
           Console.Write("Προορισμός " + (++i).ToString() + " : ");
           destination = Console.ReadLine();
           dest_lst.Add(destination);
        }while (destination != "");
        
        dest_lst.RemoveAt(dest_lst.Count - 1);
        
        Console.WriteLine("\n\nΦέτος θα πάτε:");
        foreach(string element in dest_lst){
           Console.WriteLine(" - " + element);
        }
        Console.ReadKey(true);
     }
  }
}

Σημειώστε τη χρήση του namespace “System.Collections.Generic”, στο οποίο περιλαμβάνονται όλες οι κλάσεις generic του .ΝΕΤ framework. Όπως βλέπετε στη γραμμή 7, το αντικείμενο dest_lst ορίζεται σαν ένα List που θα φιλοξενεί string. Με αυτή τη μικρή αλλαγή, δεν απαιτείται κανενός είδους type casting στο πρόγραμμά μας. Φυσικά, σε αυτό το μικρό παράδειγμα που εξετάζουμε, το να κάνουμε μια μετατροπή δυο ή τρεις φορές δεν αποτελεί πρόβλημα. Σε ένα μεγαλύτερο πρόγραμμα, με περισσότερες λίστες και πιο εκτεταμένη χρήση των δεδομένων τους, οι σχετικές μετατροπές θα προκαλούσαν πονοκέφαλο. Με τη χρήση της κλάσης generic List, αυτό το πρόβλημα απαλείφεται.

Στοίβες
Όσοι έχουν προηγούμενη προγραμματιστική εμπειρία είναι σχεδόν βέβαιο ότι έχουν ξανακούσει για τις στοίβες. Πρόκειται για μια δομή που διαχειρίζεται τα δεδομένα “σειραϊκά”, ακολουθώντας τη λογική LIFO (Last In First Out). Με άλλα λόγια, οι στοίβες μοιάζουν με λίστες στις οποίες μπορούμε να τροποποιήσουμε μόνο το ένα άκρο. Έτσι, όταν αφαιρούμε δεδομένα, ακολουθούμε υποχρεωτικά την αντίστροφη σειρά από αυτήν με την οποία έγινε η προσθήκη (από το πιο πρόσφατο στοιχείο προς το παλαιότερο). Φανταστείτε την ακριβώς σαν μια στοίβα με πιάτα. Τοποθετούμε το ένα πιάτο πάνω στο άλλο και όταν θελήσουμε να πάρουμε κάποιο, αρχίζουμε να τραβάμε πιάτα από τη κορυφή, έως ότου φτάσουμε στο επιθυμητό. Μπορεί να φαντάζει παράξενος αυτός ο τρόπος διαχείρισης, αλλά στον προγραμματισμό απαντάται πολύ συχνά και μπορεί να λύσει πολλά προβλήματα. Εξάλλου, οι στοίβες αποτελούν ένα δομικό συστατικό της αρχιτεκτονικής των επεξεργαστών. Ας μείνουμε όμως στο .ΝΕΤ και στη C#, όπου οι στοίβες υλοποιούνται με τη βοήθεια της κλάσης Stack.

Η Stack, όπως και η List, αποτελεί μια κλάση που ανήκει στην κατηγορία generic. Επομένως, κάθε φορά που δημιουργούμε ένα αντικείμενο Stack, πρέπει να δηλώσουμε τον τύπο των δεδομένων που θα διαχειρίζεται. Για να τοποθετήσουμε ένα αντικείμενο στη στοίβα πρέπει να χρησιμοποιήσουμε τη μέθοδο Push(element), ενώ για να “τραβήξουμε” ένα αντικείμενο από τη κορυφή προσφέρεται η μέθοδος Pop(). Αν δεν θέλουμε να αφαιρέσουμε το στοιχείο που βρίσκεται στην κορυφή της στοίβας και επιθυμούμε μόνο να πάρουμε ένα αντίγραφό του, πρέπει να κάνουμε χρήση της μεθόδου Peek(). Οι στοίβες αποτελούν μια ειδική μορφή λίστας και ως εκ τούτου διαθέτουν τη μέθοδο Clear(), την ιδιότητα Count κ.λπ.

Ας δούμε ένα μικρό πρόγραμμα που καταγράφει όσα οχήματα επιβιβάζονται σ’ ένα ferry boat –σαν κι αυτό που ελπίζουμε να βρισκόσαστε καθώς διαβάζετε το παρόν τεύχος. Το υποθετικό ferry boat δεν είναι αμφίπλωρο και τα οχήματα πρέπει να εξέλθουν με την αντίστροφη σειρά από αυτή με την οποία εισήλθαν. Εντελώς συμπτωματικά, το μικρό καραβάκι συμπεριφέρεται σαν μια στοίβα αυτοκινήτων.

using System;
using System.Collections.Generic;

namespace StackExample{
  class Program{
     public static void Main(string[] args){
        string plate;
        Stack<string> cars = new Stack<string>();
        
        while ((plate = Console.ReadLine()) != "") {
           cars.Push(plate);
        }
        
        int boardedCars = cars.Count;
        Console.WriteLine("Στο πλοίο μπήκαν " + boardedCars.ToString() + " οχήματα");
        Console.WriteLine("Το όχημα που μπήκε τελευταίο είναι το " + cars.Peek());
        Console.WriteLine("Τα οχήματα θα πρέπει να εξέλθουν με την εξής σειρά:");
        for(int i=0; i<boardedCars; i++){
           Console.Write((i+1).ToString() + " - " + cars.Pop());
           Console.WriteLine(" [κι απομένουν ακόμα " + cars.Count + " οχήματα εντός του πλοίου]");
        }
        
        Console.ReadKey(true);
     }
  }
}

Για κάθε όχημα θα αποθηκεύουμε τον αντίστοιχο αριθμό κυκλοφορίας. Έτσι, στη γραμμή 8 δημιουργούμε τη στοίβα cars, που μπορεί να φιλοξενεί συμβολοσειρές. Η εισαγωγή των δεδομένων πραγματοποιείται στις γραμμές 10 έως 12. Παρατηρήστε πώς έχουμε στήσει το συγκεκριμένο βρόχο. Ο έλεγχος της μεταβλητής plate, η οποία διατηρεί τον αριθμό κυκλοφορίας του εκάστοτε αυτοκινήτου, πραγματοποιείται ταυτόχρονα με τον έλεγχο επανάληψης του βρόχου. Έτσι, κάθε μη κενή συμβολοσειρά προστίθεται στη στοίβα κανονικά, ενώ μόλις ο χρήστης εισάγει ένα κενό string, ο βρόχος τερματίζεται και η στοίβα δεν τροποποιείται με κανέναν τρόπο. Ενδιαφέρον παρουσιάζει κι ο δεύτερος βρόχος του προγράμματος, που τυπώνει τα περιεχόμενα της στοίβας. Όπως θα παρατηρήσατε, για τον έλεγχο του μετρητή (του βρόχου) δεν χρησιμοποιούμε την ιδιότητα Count. Ο μετρητής του βρόχου συγκρίνεται με τη μεταβλητή boardedCars, στην οποία αποθηκεύσαμε την τιμή της ιδιότητας Count όταν ολοκληρώθηκε η εισαγωγή των αυτοκινήτων. Αναρωτιέστε γιατί κάναμε όλη αυτή τη φασαρία; Κάθε φορά που εκτελείται ο βρόχος, το στοιχείο της στοίβας που βρίσκεται στην κορυφή αφαιρείται και κατά συνέπεια η ιδιότητα Count μεταβάλλεται.

Άσκηση για την παραλία: Πόσες φορές θα εκτελεστεί ο βρόχος για οκτώ εισαχθέντα οχήματα, αν χρησιμοποιήσουμε την ιδιότητα Count για τον έλεγχο του μετρητή;

Αχά! Εδώ έχουμε να κάνουμε είτε με ένα πολύ μικρό ferry boat είτε με κάποιον προορισμό στην άγονη γραμμή. Όπως και να 'χει, τα αυτοκίνητα πρέπει να αποβιβαστούν με τη συγκεκριμένη σειρά.

Ουρές
Οι ουρές (queues) αποτελούν ένα είδος λίστας, στο οποίο τα δεδομένα κινούνται και πάλι σειραϊκά. Η διαφορά τους από τις στοίβες έγκειται στον τρόπο με τον οποίο εξέρχονται τα δεδομένα. Φανταστείτε ότι σε αυτές τις λίστες μπορούμε να προσθέτουμε στοιχεία στη μία άκρη και να αφαιρούμε από την άλλη. Αυτό έχει σαν αποτέλεσμα να παίρνουμε πάντα το παλιότερο διαθέσιμο στοιχείο. Οι ουρές υλοποιούν τη λογική First In First Out, ή όπως συνηθίζουμε να λέμε στο Μαύρο Γιαλό της Χίου, FIFO.

Για την υλοποίηση μιας ουράς μπορούμε να χρησιμοποιήσουμε την κλάση Queue, που ανήκει επίσης στην κατηγορία generic. Για την εισαγωγή στοιχείων στην ουρά διατίθεται η μέθοδος Enqueue(element), ενώ για την αφαίρεση στοιχείων η Dequeue(). Για να διαβάσουμε το στοιχείο που βρίσκεται στην έξοδο της λίστας χωρίς να το διαγράψουμε, προσφέρεται και πάλι η μέθοδος Peek(). Στο παράδειγμα που ακολουθεί κάνουμε χρήση της κλάσης Queue, ώστε να προσομοιάσουμε τον τρόπο εξυπηρέτησης των πελατών σε μια καντίνα. Και όχι σε καμιά τυχαία καντίνα, αλλά σ’ αυτή που βρίσκεται στο Μαύρο Γιαλό (της Χίου).

using System;
using System.Collections.Generic;

namespace QueueExample{
  class Program{
     public static void Main(string[] args){
        Queue<string> customers = new Queue<string>();
        string name;
        
        while ((name = Console.ReadLine()) != ""){
           customers.Enqueue(name);
        }
        
        int customersSum = customers.Count;
        Console.WriteLine(customersSum.ToString() + " πελάτες περιμένουν να εξυπηρετηθούν");
        Console.WriteLine("Η σειρά εξυπηρέτησης είναι:");
        for(int i=0; i<customersSum; i++){
           Console.Write((i+1).ToString() + " - " + customers.Dequeue());
           Console.WriteLine(" [και απομένουν ακόμα " + customers.Count + " στη σειρά]");
        }
        
        Console.ReadKey(true);
     }
  }
}

Ο κώδικας είναι ίδιος με εκείνον που είδαμε στο προηγούμενο παράδειγμα. Το μόνο που αλλάζει είναι τα ονόματα των μεταβλητών και, φυσικά, η συμπεριφορά της βασικής δομής δεδομένων που τώρα είναι η ουρά.

Ένας, ένας παρακαλούμε! Όλοι θα πάρετε το παγωτάκι σας :)

Λεξικά
Για το τέλος αφήσαμε μια ακόμα ενδιαφέρουσα δομή δεδομένων, που ονομάζεται λεξικό (dictionary). Τα λεξικά αποτελούν επίσης μια μορφή λίστας, για κάθε στοιχείο της οποίας αποθηκεύεται ένα ζεύγος κλειδιού–τιμής. Όπως υποψιάζεστε, τα λεξικά επιτρέπουν την ανάκτηση οποιασδήποτε τιμής και λειτουργούν κατ’ ανάγκη σειραϊκά. Για να πάρουμε μια τιμή αρκεί να χρησιμοποιήσουμε το αντίστοιχο κλειδί ως δείκτη, όπως ακριβώς κάνουμε και με τα associative arrays στην PHP. Για την υλοποίηση των λεξικών προσφέρεται η κλάση Dictionary, που ανήκει και πάλι στην κατηγορία generic. Κατά τη δημιουργία ενός λεξικού πρέπει να δηλώσουμε τύπο δεδομένων για τα κλειδιά, όπως επίσης και για τις τιμές. Για παράδειγμα, δείτε τη δημιουργία ενός λεξικού που θα αποθηκεύει το κόστος για διάφορους τύπους καυσίμων:

Dictionary<string, float> fuel = new Dictrionary<string,float>;

Το όνομα του εκάστοτε καυσίμου θα λειτουργεί ως κλειδί (δείκτης) του λεξικού, ενώ το αντίστοιχο κόστος θα αποτελεί μια τιμή του λεξικού. Έτσι, επιλέξαμε τον τύπο string για τα κλειδιά και τον τύπο float για τις τιμές. Η προσθήκη ζευγαριών (κλειδί–τιμή) σε ένα λεξικό πραγματοποιείται με τη μέθοδο Add(key,value). Συνεχίζοντας το προηγούμενο παράδειγμα, δείτε την προσθήκη του κόστους για την αμόλυβδη 95άρα βενζίνη:

fuel.Add("regular95", 1.678);

Φωτιά και λάβρα! Για την τιμή λέμε, διότι ο καιρός είναι ακόμα ήπιος για την εποχή. Για να πάρουμε μια τιμή από το λεξικό χρησιμοποιούμε τη σύνταξη που έχουμε δει στους πίνακες, με τη μόνη διαφορά ότι ο δείκτης δεν είναι πάντα αριθμητικός αλλά έχει τον τύπο που έχουμε επιλέξει για τα κλειδιά του λεξικού. Στη συγκεκριμένη περίπτωση, οι δείκτες είναι τύπου string. Δείτε πώς θα διαβάζαμε το κόστος για την αμόλυβδη 95άρα:

float price = fuel["regular95"];

Στην περίπτωση που δεν ξέρουμε αν υπάρχει κάποιο συγκεκριμένο κλειδί στο λεξικό, μπορούμε να στραφούμε στη μέθοδο ContainsKey(key). Αυτή η μέθοδος πραγματοποιεί μια γρήγορη αναζήτηση για το δοσμένο κλειδί και ανάλογα με το αν θα το εντοπίσει επιστρέφει true ή false. Ομοίως, μπορούμε να κάνουμε αναζήτηση και για την ύπαρξη μιας συγκεκριμένης τιμής, αξιοποιώντας τη μέθοδο ContainsValue(val).

Console.WriteLine(fuel.ContainsKey(&quot;regular95&quot;));
>&gt; true
Console.WriteLine(fuel.ContainsValue(&quot;1.10&quot;));
>&gt; false

Για την αφαίρεση κάποιου στοιχείου από ένα λεξικό προσφέρεται η μέθοδος Remove(key). Ας εξετάσουμε όμως ένα πιο ολοκληρωμένο παράδειγμα. Το πρόγραμμα που ακολουθεί χρησιμοποιεί ένα λεξικό για να αποθηκεύσει το πλήθος των μπάνιων που έκανε κάθε μέλος μιας μεγάλης παρέας. Σαν εφαρμογή θα την χαρακτηρίζαμε παιδαριώδη, αλλά σαν πρόγραμμα έχει ιδιαίτερο ενδιαφέρον αφού επιδεικνύει την ευελιξία που προσφέρουν τα λεξικά:

using System;
using System.Collections.Generic;

namespace DictionaryExample{
  class Program{
     public static void Main(string[] args){
        Dictionary<string,int> mpania = new Dictionary<string,int>();
        string selection = "";
        Console.Title = "dH044 - DictionaryExample - Τα μπάνια του λαού";
        do{
           Console.Clear();
           Console.WriteLine("[1] Προσθήκη Μπάνιου");
           Console.WriteLine("[2] Αναζήτηση");
           Console.WriteLine("[3] Προβολή όλων");
           Console.WriteLine("[4] Έξοδος");
           Console.Write("\nΔώστε την επιλογή σας:");
           selection = Console.ReadLine();         
           if(selection.StartsWith("1")){
              Console.Clear();
              Console.Write("Δώστε το όνομά σας για να προστεθεί ένα μπάνιο!:");
              string name = Console.ReadLine();
              if(mpania.ContainsKey(name)){
                 mpania[name]++;
              }else{
                 mpania[name] = 1;
              }
              Console.WriteLine("ΟΚ " + name + ". Ένα μπάνιο προστέθηκε στο λογαριασμό σου!");
              Console.WriteLine("\nΠατήστε ένα οποιοδήποτε πλήκτρο για συνέχεια...");
              Console.ReadKey();
           }else if (selection.StartsWith("2")) {
              Console.Clear();
              Console.Write("Δώστε το όνομα προς αναζήτηση:");
              string name = Console.ReadLine();
              if(mpania.ContainsKey(name)){
                 Console.WriteLine("O/H " + name + " έχει κάνει " + mpania[name].ToString() + " μπάνια μέχρι στιγμής!");
              }else{
                 Console.WriteLine("Δεν βρέθηκε καταχώριση με αυτό το όνομα!");
              }
              Console.WriteLine("\nΠατήστε ένα οποιοδήποτε πλήκτρο για συνέχεια...");
              Console.ReadKey();
           }else if(selection.StartsWith("3")){
              Console.Clear();
              if(mpania.Count > 0){
                 foreach(KeyValuePair<string, int> friend in mpania){
                    Console.WriteLine(friend.Key + " : " + friend.Value.ToString() + " μπάνια");
                 }
              }else{
                 Console.WriteLine("Δεν υπάρχουν καταχωρίσεις προς εμφάνιση!");
              }
              Console.WriteLine("\nΠατήστε ένα οποιοδήποτε πλήκτρο για συνέχεια...");
              Console.ReadKey();
           }
        }while(!selection.StartsWith("4"));
     }
  }
}

Η κεντρική οθόνη επιλογών του προγράμματός μας.

Πιστεύουμε ότι ο κώδικας δεν χρειάζεται ιδιαίτερη επεξήγηση. Το μόνο σημείο που παρουσιάζει ξεχωριστό ενδιαφέρον είναι ο βρόχος που ξεκινάει στη γραμμή 43. Εκεί θα παρατηρήσετε ότι για κάθε στοιχείο του λεξικού παίρνουμε ένα structure KeyValuePair. Πρόκειται για μια δομή δεδομένων που ορίζουμε επιτόπου κι απαρτίζεται από δύο στοιχεία (ένα string και ένα int). Στο στοιχείο KeyValuePair.Key αποθηκεύεται το κλειδί του εκάστοτε ζεύγους, ενώ στο KeyValuePair.Value αποθηκεύεται η τιμή που αντιστοιχεί στο εκάστοτε κλειδί.

Οι δομές δεδομένων που εξετάσαμε είναι εξαιρετικά χρήσιμες και σε επόμενα τεύχη θα μας βοηθήσουν στην ανάπτυξη ρεαλιστικών εφαρμογών. Όπως πάντα, μπορείτε να κατεβάσετε ένα πακέτο με τον κώδικα των παραδειγμάτων από το http://bit.ly/dh044dnetp6dl. Μέχρι το επόμενο τεύχος σας ευχόμαστε να περνάτε καλά και να μην αφήνετε τίποτα να χαλάει την καλοκαιρινή σας διάθεση!

Ο Πέτρος έκανε ένα ακόμα μπάνιο και έσπευσε να το καταχωρίσει! Ούτε μικρό παιδί να ήταν.

Έτσι εξηγείται η χαρά του Πέτρου! Το μπάνιο που καταχώρισε ήταν το πρώτο της χρονιάς.

Ιδού και τα αποτελέσματα για όλη την παρέα. Ίσως τελικά να μην έχει μείνει και τόσο πίσω ο Πέτρος. Ένα μπάνιο είναι πολύ μικρή διαφορά!

Leave a Reply

You must be logged in to post a comment.

Σύνδεση

Αρχείο δημοσιεύσεων