Διαδρομές
/
C++
C++
/
Ασκήσεις
/
Τρολάρισε τα τρολ
Τρολάρισε τα τρολ

Τρολάρισε τα τρολ

Άσκηση εκμάθησης

Εισαγωγή

Απαριθμήσεις

Μέχρι τώρα έχουμε ασχοληθεί κυρίως με αριθμούς και συμβολοσειρές. Για να μοντελοποιήσουμε τον πραγματικό κόσμο, μπορεί να θέλουμε έναν περιορισμένο αριθμό τιμών που μπορεί να πάρει μια μεταβλητή. Μπορεί να θέλεις έναν ξεχωριστό τύπο με λίγες διακριτές τιμές που έχουν ξεχωριστά ονόματα. Για παράδειγμα, σε ένα εργοστάσιο σκέιτμπορντ, το υλικό της σανίδας να είναι επιλογή ανάμεσα μόνο σε maple, bamboo ή plastic.

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

Παράδειγμα χρήσης

Ο ειδικός όρος για αυτού του είδους την απαρίθμηση είναι scoped enumeration. Το παρακάτω απόσπασμα δείχνει πώς να γράψεις μια enumeration με όνομα DeckMaterial. Πρόσεξε τη λέξη-κλειδί enum class και το ; στο τέλος του ορισμού:

enum class DeckMaterial {
    maple,
    bamboo,
    plastic
};

Τώρα κοίτα μια συνάρτηση υπολογισμού τιμής στο κατάστημα σκέιτμπορντ και πρόσεξε τον τελεστή προσδιορισμού εμβέλειας (::) που προσδιορίζει έναν enumerator από την enumeration:

double deck_price(double base_price, DeckMaterial material) {
    if(material == DeckMaterial::plastic) {
        return base_price * 0.9;
    }
    return base_price * 1.3;
}

Εμβέλεια

Φαντάσου ότι έχεις μια δεύτερη enumeration για το υλικό των τροχών:

enum class WheelMaterial {
    steel,
    clay,
    plastic
};

Αν και οι τροχοί και η σανίδα μπορούν και τα δύο να είναι από plastic, δεν μπορούν να μπερδευτούν μεταξύ τους. Είναι διαφορετικοί τύποι: το plastic του DeckMaterial και το plastic του WheelMaterial. Κάθε enumeration θα έχει τους enumerators της στη δική της εμβέλεια, στο δικό της namespace. Γι' αυτόν τον λόγο ονομάζονται scoped enumerations.

Advanced

Απαριθμήσεις χωρίς εμβέλεια

Μπορεί να σκέφτεσαι ότι με ένα όνομα σαν το scoped θα υπήρχαν και unscoped απαριθμήσεις, και θα είχες δίκιο. Οι Unscoped enumerations γίνονται όλο και λιγότερο δημοφιλείς, επειδή όλες μοιράζονται το ίδιο καθολικό namespace. Εξαιτίας αυτού του διαμοιρασμού, δεν θα μπορούσες να έχεις δύο unscoped enumerations με τους ίδιους enumerators όπως το plastic στο παραπάνω παράδειγμα.

Επίσης, οι unscoped enumerations μετατρέπονται σιωπηρά σε ακέραιους. Κοίτα το παρακάτω παράδειγμα για ένα αναπάντεχο αποτέλεσμα:

enum CitrusFruits {
    lemons,  // 0
    oranges, //1
};

enum IceCream {
    walnut,  // 0
    apples,  // 1
};

bool comparison{apples == oranges};
// => true

// Example from above:
bool comparison{DeckMaterial::plastic == WheelMaterial::plastic};
// => Does not compile!

Αν θέλεις να μετατρέψεις scoped enumerations σε ακέραιους, μπορείς να χρησιμοποιήσεις το static_cast<int>.

Εντολές switch

Όπως και άλλες γλώσσες, η C++ παρέχει επίσης μια εντολή switch. Οι εντολές switch είναι ένας συντομότερος τρόπος να γράψεις μεγάλες ακολουθίες εντολών if ... else if. Για να φτιάξεις μια εντολή switch, ξεκινάς χρησιμοποιώντας τη λέξη-κλειδί switch, ακολουθούμενη από έναν ακέραιο. Στη συνέχεια δηλώνεις καθεμία από τις συνθήκες με τη λέξη-κλειδί case. Μπορείς επίσης να δηλώσεις μια περίπτωση default, η οποία θα εκτελεστεί όταν καμία από τις προηγούμενες συνθήκες case δεν ταιριάξει. Κάθε περίπτωση πρέπει να τελειώνει με μια εντολή break (ή return).

int price{0};
int adults{3};
int kids{2};

switch (int group_size{adults + kids}) {
    case 1:
        price = 50;
        break;
    case 2:
        price = 70;
        break;
    default:
        price = group_size * 30;
}

Διάβαση στην επόμενη περίπτωση

Κάτι σημαντικό για τη δομή switch είναι ότι ο κώδικας συνεχίζει να εκτελείται μέχρι να τον σταματήσει μια εντολή break (ή return). Αυτό μπορεί να οδηγήσει σε απρόσμενη συμπεριφορά.

int adults{1};
int kids{0};
switch (int group_size{adults + kids}) {
    case 1:
        price = 50;
    case 2:
        price = 70;
    default:
        price = group_size * 30;
}
// price will be 30!

Η κύρια περίπτωση χρήσης αυτής της δυνατότητας συνέχειας της εκτέλεσης είναι μια εντολή που έχει πολλές ετικέτες. Πολλά αποτελέσματα του switch μπορούν να αντιστοιχούν στο ίδιο κομμάτι κώδικα προς εκτέλεση. Έτσι, σε μια εφαρμογή κρατήσεων για παράδειγμα, η συνάρτηση που καλείται για μεγέθη γκρουπ 2 και 3 μπορεί να είναι η ίδια:

switch (group_size) {
case 1:
    book_room();
    break;
case 2:
case 3:
    book_apartment(group_size);
    break;
default:
    book_house(group_size);
}
// book_apartment happens when group_size is 2 or 3

Οδηγίες

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

Το φόρουμ υποστηρίζει τρεις διαφορετικές ενέργειες:

  • Ανάγνωση
  • Εγγραφή
  • Διαγραφή

Υπάρχουν τέσσερις τύποι λογαριασμών, ο καθένας με διαφορετικά προεπιλεγμένα δικαιώματα:

  • Επισκέπτες: μπορούν να διαβάζουν αναρτήσεις.
  • Χρήστες και Τρόλ: μπορούν να διαβάζουν και να γράφουν αναρτήσεις.
  • Συντονιστές: μπορούν να διαβάζουν, να γράφουν και να διαγράφουν αναρτήσεις, έχουν όλα τα δικαιώματα.

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

Αυτή η πρακτική ονομάζεται shadow-banning.

1. Ρύθμισε τους τύπους χρηστών και τα δικαιώματα.

Πρώτα, όρισε μια απαρίθμηση AccountStatus που να αντιπροσωπεύει τους τέσσερις τύπους λογαριασμών: troll, guest, user και mod.

Έπειτα, όρισε μια απαρίθμηση Action που να αντιπροσωπεύει τους τρεις τύπους δικαιωμάτων: read, write και remove.

2. Πρόβαλε τα τρόλ μόνο σε άλλα τρόλ.

Κάθε ανάρτηση στα φόρουμ αποθηκεύει το AccountStatus αυτού που την ανάρτησε στα μεταδεδομένα της. Βεβαιώσου ότι οι αναρτήσεις από τρόλ εμφανίζονται μόνο σε άλλα τρόλ.

Η Helma χρειάζεται μια συνάρτηση display_post, που δέχεται δύο ορίσματα τύπου AccountStatus και επιστρέφει ένα bool. Το πρώτο όρισμα είναι η κατάσταση αυτού που ανάρτησε, το δεύτερο η κατάσταση αυτού που τη βλέπει.

using namespace hellmath;
display_post(AccountStatus::troll, AccountStatus::user);
// => false
display_post(AccountStatus::mod, AccountStatus::guest);
// => true

3. Έλεγξε τα δικαιώματα για διαφορετικούς χρήστες.

Η Helma χρειάζεται έναν τρόπο να ελέγχει αν μια συγκεκριμένη ενέργεια επιτρέπεται για έναν χρήστη.

Υλοποίησε μια συνάρτηση permission_check, που δέχεται ένα Action ως πρώτο όρισμα και ένα AccountStatus με το οποίο θα γίνει ο έλεγχος. Θα πρέπει να επιστρέφει ένα bool σύμφωνα με τα δικαιώματα που αναφέρονται στην εισαγωγή.

permission_check(Action::remove, AccountStatus::guest);
// => false
permission_check(Action::write, AccountStatus::mod);
// => true

4. Χορήγησε πρόσβαση στο παιχνίδι και ταίριαξε παίκτες.

Για να κρατάει τους πραγματικούς παίκτες του παιχνιδιού υπόλογους για τις πράξεις τους, το Hellmath αρνείται την πρόσβαση στους επισκέπτες. Όπως αναφέρθηκε παραπάνω, η Helma θέλει τα τρόλ να τρολάρουν άλλα τρόλ. Οι συνδέσεις παιχνιδιού μεταξύ των άλλων χρηστών δεν έχουν περιορισμούς.

Υλοποίησε τη συνάρτηση valid_player_combination που ελέγχει αν δύο παίκτες μπορούν να μπουν στο ίδιο παιχνίδι. Η συνάρτηση έχει δύο παραμέτρους τύπου AccountStatus και επιστρέφει ένα bool.

valid_player_combination(AccountStatus::guest, AccountStatus::mod);
// => false
valid_player_combination(AccountStatus::troll, AccountStatus::troll);
// => true

5. Υλοποίησε ουρά προτεραιότητας.

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

Υλοποίησε τη συνάρτηση has_priority που δέχεται δύο ορίσματα AccountStatus και επιστρέφει true, αν και μόνο αν ο πρώτος λογαριασμός έχει αυστηρά υψηλότερη προτεραιότητα από τον δεύτερο.

has_priority(AccountStatus::guest, AccountStatus::mod);
// => false
has_priority(AccountStatus::user, AccountStatus::troll);
// => true
Επεξεργασία μέσω GitHub Ο σύνδεσμος ανοίγει σε νέο παράθυρο ή καρτέλα
C++ Exercism

Έτοιμος να ξεκινήσεις την άσκηση Τρολάρισε τα τρολ;

Γράψου στο Exercism για να μάθεις και να κατακτήσεις C++ με 19 έννοιες100 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.