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

Μάστορας της λαζάνια

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

Εισαγωγή

Ορολογία

Έχεις ήδη χρησιμοποιήσει και γράψει συναρτήσεις C++ σε μερικές έννοιες. Ήρθε η ώρα να γίνουμε τεχνικοί. Το παρακάτω απόσπασμα κώδικα δείχνει τους πιο συνηθισμένους όρους για εύκολη αναφορά. Επειδή η C++ αγνοεί τα κενά διαστήματα, η μορφοποίηση έχει αλλάξει ώστε κάθε στοιχείο να βρίσκεται σε μία γραμμή.

// Function declaration:
bool                                              // Return type
admin_detected(string user, string password)      // Type signature
;                                                 // Don't forget the ';' for the declaration

// Function definition:
bool                                              // Return type
admin_detected                                    // Function name
(string user, string password)                    // Parameter list
{ return user == "admin" && password == "1234"; } // Function body
Advanced

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

Ο προσδιοριστής const

Μερικές φορές θέλεις να βεβαιωθείς ότι οι τιμές δεν μπορούν να αλλάξουν αφού έχουν αρχικοποιηθεί. Η C++ χρησιμοποιεί τη λέξη-κλειδί const ως προσδιοριστή για σταθερές.

const int number_of_dragon_balls{7};
number_of_dragon_balls--; // compilation error
Note

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

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

Μπορείς επίσης να έχεις το const ως προσδιοριστή για παραμέτρους συνάρτησης.

string guess_number(const int& secret, const int& guess) {
    if (secret < guess) return "lower.";
    if (secret > guess) return "higher.";
    return "exact!";
}

Όταν περνάς μια αναφορά const στη συνάρτηση, μπορείς να είσαι σίγουρος ότι θα μείνει αμετάβλητη. Θα δεις συχνά αναφορές const για αντικείμενα των οποίων το αντίγραφο μπορεί να είναι δαπανηρό, όπως οι μεγαλύτερες συμβολοσειρές. Μια τρίτη περίπτωση χρήσης για τον προσδιοριστή const είναι οι συναρτήσεις μέλους που δεν αλλάζουν το στιγμιότυπο μιας κλάσης.

class Stubborn {
    public:
    Stubborn(string reply) {
        response = reply;
    }
    string answer(const string& question) const {
        if (question.length() == 0) { return ""; }
        return response;
    }
    private:
    string response{};
};

Η συνάρτηση μέλους answer της Stubborn χρησιμοποιεί μια αναφορά const string& ως παράμετρο. Αυτό αποφεύγει μια πράξη αντιγραφής από το αρχικό αντικείμενο που πέρασε στη συνάρτηση.

Υπερφόρτωση συναρτήσεων

Πολλές συναρτήσεις μπορούν να έχουν το ίδιο όνομα, αν η λίστα παραμέτρων είναι διαφορετική. Αυτό λέγεται υπερφόρτωση συναρτήσεων και γίνεται συνήθως όταν αυτές οι συναρτήσεις εκτελούν πολύ παρόμοιες εργασίες.

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

Το παράδειγμα play_sound έχει έξι διαφορετικές υπερφορτώσεις για να καλύψει διαφορετικά σενάρια:

// different argument types:
void play_sound(char note);         // C, D, E, ..., B
void play_sound(string solfege);    // do, re, mi, ..., ti
void play_sound(int jianpu);        // 1, 2, 3, ..., 7

// different number of arguments:
void play_sound(string solfege, double duration);

// different qualifiers:
void play_sound(vector<string>& solfege);
void play_sound(const vector<string>& solfege);
Advanced

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

Προεπιλεγμένα ορίσματα

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

void record_new_horse_birth(string name, int weight, string color="brown-ish", string dam="Alruccaba", string sire="Poseidon");

record_new_horse_birth("Urban Sea", 130); // color will be brown, dam "Alruccabam", sire "Poseidon"
record_new_horse_birth("Highclere", 175, "off-white", "Fall Aspen");   // sire will be "Poseidon"

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

Οδηγίες

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

Έχεις πέντε εργασίες. Η πρώτη σχετίζεται με το ίδιο το μαγείρεμα, οι άλλες τέσσερις με την τέλεια προετοιμασία.

1. Υπολόγισε τον χρόνο προετοιμασίας

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

Υλοποίησε μια συνάρτηση preparationTime που δέχεται ένα διάνυσμα με τα στρώματα ως string και τον μέσο χρόνο προετοιμασίας ανά στρώμα σε λεπτά ως int. Η συνάρτηση πρέπει να επιστρέφει την εκτίμηση για τον συνολικό χρόνο προετοιμασίας με βάση τον αριθμό των στρωμάτων ως int. Αν δεν περάσεις τον μέσο χρόνο προετοιμασίας, τότε θα πρέπει να χρησιμοποιηθεί η προεπιλεγμένη τιμή 2.

std::vector<std::string> layers{"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"};
preparationTime(layers, 3)
// => 18
preparationTime(layers)
// => 12

2. Υπολόγισε τις ποσότητες νουντλς και σάλτσας που χρειάζεσαι

Εκτός από το να κρατήσεις τον χρόνο, θέλεις επίσης να βεβαιωθείς ότι έχεις αρκετή σάλτσα και νουντλς για να φτιάξεις τη λαζάνια των ονείρων σου. Για κάθε στρώμα νουντλς στη λαζάνια σου, θα χρειαστείς 50 γραμμάρια νουντλς. Για κάθε στρώμα σάλτσας στη λαζάνια σου, θα χρειαστείς 0,2 λίτρα σάλτσας.

Όρισε τη συνάρτηση quantities που παίρνει ως παράμετρο ένα διάνυσμα με τα στρώματα ως string. Η συνάρτηση θα προσδιορίζει τότε την ποσότητα νουντλς και σάλτσας που χρειάζεται για να φτιάξεις το γεύμα σου. Το αποτέλεσμα πρέπει να επιστραφεί ως amount struct, που έχει ήδη οριστεί για σένα. Το amount έχει δύο μεταβλητές-μέλη: η μία για το βάρος των noodles ως int και η δεύτερη για τον όγκο της sauce ως double.

struct amount { int noodles; double sauce; };

amount needed{quantities({"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"})};
// needed.noodles => 100
// needed.sauce => 0.4

3. Πρόσθεσε το μυστικό συστατικό

Πριν λίγο καιρό επισκέφτηκες έναν φίλο και έφαγες λαζάνια εκεί. Ήταν καταπληκτική και είχε κάτι το ξεχωριστό. Ο φίλος σου έστειλε τη λίστα με τα υλικά και σου είπε ότι το τελευταίο στοιχείο της λίστας είναι το "μυστικό συστατικό" που έκανε το γεύμα τόσο ξεχωριστό. Τώρα θέλεις να προσθέσεις κι εσύ αυτό το μυστικό συστατικό στη συνταγή σου.

Γράψε μια συνάρτηση addSecretIngredient που δέχεται δύο vector of strings ως παραμέτρους. Η πρώτη παράμετρος είναι μια αναφορά στα υλικά της δικής σου συνταγής, και το δεύτερο διάνυσμα συμβολοσειρών περιέχει τα υλικά που σου έστειλε ο φίλος σου. Το τελευταίο στοιχείο στη λίστα με τα υλικά σου είναι πάντα "?". Η συνάρτηση πρέπει να το αντικαταστήσει με το τελευταίο στοιχείο της λίστας του φίλου σου. Σημείωση: η addSecretIngredient δεν επιστρέφει τίποτα - πρέπει να τροποποιήσεις απευθείας τη λίστα με τα υλικά σου. Η λίστα με τα υλικά του φίλου σου μπορεί επίσης να είναι αναφορά (για βελτίωση της απόδοσης), αλλά δεν πρέπει να τροποποιηθεί. Βεβαιώσου ότι η λίστα δεν θα τροποποιηθεί, χρησιμοποιώντας μια αναφορά const.

std::vector<std::string> friendsList{"noodles", "sauce", "mozzarella", "kampot pepper"};
std::vector<std::string> myList{"noodles", "meat", "sauce", "mozzarella", "?"};

addSecretIngredient(myList, friendsList);
// myList => {"noodles", "meat", "sauce", "mozzarella", "kampot pepper"}

4. Κλιμάκωσε τη συνταγή

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

Υλοποίησε μια συνάρτηση scaleRecipe που παίρνει δύο παραμέτρους.

  • Ένα διάνυσμα με τις ποσότητες double που χρειάζονται για 2 μερίδες.
  • Τον αριθμό των μερίδων που θέλεις να μαγειρέψεις.

Η συνάρτηση πρέπει να επιστρέφει ένα νέο διάνυσμα με τις ποσότητες που χρειάζονται για τον επιθυμητό αριθμό μερίδων. Θέλεις όμως να κρατήσεις την αρχική συνταγή. Αυτό σημαίνει ότι το όρισμα quantities δεν πρέπει να τροποποιηθεί μέσα σε αυτή τη συνάρτηση.

std::vector<double> quantities{1.2, 3.6, 10.5};
scaleRecipe(quantities, 4);
// => { 2.4, 7.2, 21 }

5. Ξεκλείδωσε το οικογενειακό μυστικό

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

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

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

std::vector<std::string> myList{"noodles", "meat", "sauce", "mozzarella", "?"};
std::string secretIngredient{"spice melange"};
addSecretIngredient(myList, secretIngredient);
// myList => {"noodles", "meat", "sauce", "mozzarella", "spice melange"}

Καλή τύχη, γενναίε σεφ!

Επεξεργασία μέσω GitHub Ο σύνδεσμος ανοίγει σε νέο παράθυρο ή καρτέλα
C++ Exercism

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

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