Track
/
C++
C++
/
Esercizi
/
Maestro di lasagna
Maestro di lasagna

Maestro di lasagna

Esercizio di apprendimento

Introduzione

Terminologia

Hai già usato e scritto funzioni C++ in un paio di concetti. È ora di entrare nei dettagli tecnici. Il frammento di codice qui sotto mostra i termini più comuni, per una consultazione rapida. Dato che C++ ignora gli spazi bianchi, la formattazione è stata modificata per mettere ogni elemento su una riga separata.

// 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

La dichiarazione funziona come una nota per il compilatore: esiste una funzione con quel nome, quel tipo restituito e quella lista di parametri. Il codice non funzionerà se manca la definizione. Le dichiarazioni sono opzionali: servono se usi la funzione prima della sua definizione. Le dichiarazioni possono risolvere problemi come i riferimenti ciclici e si possono usare per separare l'interfaccia dall'implementazione.

Il qualificatore const

A volte vuoi assicurarti che i valori non possano essere modificati dopo essere stati inizializzati. C++ usa la parola chiave const come qualificatore per le costanti.

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

Spesso vedrai le costanti scritte in UPPER_SNAKE_CASE. Si consiglia di riservare questo tipo di maiuscole alle macro, se non esiste un'altra convenzione.

Se provi a modificare una variabile costante dopo che è stata impostata, il codice non verrà compilato. Questo aiuta a evitare modifiche indesiderate, ma apre anche possibilità di ottimizzazione per il compilatore. Per una persona è anche più facile ragionare sul codice, se sa che certe parti non verranno toccate.

Puoi anche usare const come qualificatore per i parametri di una funzione.

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

Quando passi un riferimento const alla funzione, puoi stare certo che rimarrà invariato. Vedrai spesso riferimenti const per oggetti che potrebbero essere costosi da copiare, come le stringhe più lunghe. Un terzo caso d'uso del qualificatore const sono le funzioni membro che non modificano l'istanza di una classe.

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

La funzione membro answer di Stubborn usa un riferimento const string& come parametro. In questo modo si evita un'operazione di copia dell'oggetto originale passato alla funzione.

Overloading di funzioni

Più funzioni possono avere lo stesso nome se la lista dei parametri è diversa. Questa si chiama overloading di funzioni e si usa di solito quando queste funzioni svolgono compiti molto simili.

L'intestazione della funzione senza il tipo restituito è la firma di tipo della funzione. Un cambiamento nella firma di tipo dà origine a una nuova funzione.

L'esempio di play_sound ha sei overload diversi per adattarsi a scenari diversi:

// 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

La firma di tipo è definita dal nome della funzione, dal numero di parametri, dai loro tipi e dai loro qualificatori (ma non dai loro nomi). Il tipo restituito non fa esplicitamente parte della firma di tipo e otterrai errori di compilazione se hai due funzioni che differiscono solo per il tipo restituito. Il compilatore si lamenterà perché non è chiaro quale delle due debba essere usata.

Argomenti predefiniti

Alcune funzioni possono diventare molto lunghe e molte delle loro chiamate potrebbero usare gli stessi valori per la maggior parte dei parametri. La ripetizione in quelle chiamate si può evitare con gli argomenti predefiniti.

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"

Dato che la dichiarazione della funzione viene spesso letta prima della definizione, è il posto migliore dove impostare gli argomenti predefiniti. Se un parametro ha un valore predefinito, anche tutti i parametri alla sua destra devono averne uno. A volte gli overload complicati di una funzione si possono riorganizzare in meno funzioni con argomenti predefiniti, per migliorare la manutenibilità.

Istruzioni

In questo esercizio scriverai altro codice per preparare e cucinare la tua fantastica lasagna, seguendo il tuo libro di ricette preferito.

Ci sono cinque attività. La prima riguarda la cottura vera e propria, le altre quattro la preparazione perfetta.

1. Stima il tempo di preparazione

Per la prossima lasagna che preparerai, vuoi assicurarti di avere abbastanza tempo a disposizione per goderti la cottura. Hai già deciso quali strati avrà la tua lasagna. Ora vuoi stimare quanto tempo richiederà la preparazione, in base a questo.

Implementa una funzione preparationTime che accetta un vettore di strati come string e il tempo medio di preparazione per strato in minuti come int. La funzione deve restituire la stima del tempo totale di preparazione, calcolata in base al numero di strati, come int. Se il tempo medio di preparazione non viene passato, va usato il valore predefinito 2.

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

2. Calcola le quantità di noodles e salsa necessarie

Oltre a riservare il tempo, vuoi anche assicurarti di avere abbastanza salsa e noodles per cucinare la lasagna dei tuoi sogni. Per ogni strato di noodles nella tua lasagna ti serviranno 50 grammi di noodles. Per ogni strato di salsa ti serviranno 0,2 litri di salsa.

Definisci la funzione quantities che prende come parametro un vettore di strati di tipo string. La funzione determinerà poi la quantità di noodles e salsa necessaria per preparare il tuo pasto. Il risultato va restituito come una struct amount, già definita per te. amount ha due variabili membro: una per il peso dei noodles come int e la seconda per il volume della sauce come double.

struct amount { int noodles; double sauce; };

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

3. Aggiungi l'ingrediente segreto

Tempo fa hai fatto visita a un amico e hai mangiato la lasagna a casa sua. Era fantastica e aveva qualcosa di speciale. L'amico ti ha mandato la lista degli ingredienti e ti ha detto che l'ultimo elemento della lista è l'«ingrediente segreto» che ha reso il pasto così speciale. Ora vuoi aggiungere anche tu quell'ingrediente segreto alla tua ricetta.

Scrivi una funzione addSecretIngredient che accetta due vector of strings come parametri. Il primo parametro è un riferimento agli ingredienti della tua ricetta, e il secondo vettore di stringhe contiene gli ingredienti che ti ha mandato il tuo amico. L'ultimo elemento della tua lista di ingredienti è sempre "?". La funzione deve sostituirlo con l'ultimo elemento della lista del tuo amico. Nota: addSecretIngredient non restituisce nulla: devi modificare direttamente la lista dei tuoi ingredienti. Anche la lista con gli ingredienti del tuo amico può essere un riferimento (per migliorare le prestazioni), ma non deve essere modificata. Assicurati che la lista non venga modificata, usando un riferimento 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. Ridimensiona la ricetta

Le quantità indicate nel tuo libro di ricette bastano per una lasagna di due porzioni. Dato che la prossima volta vuoi cucinare per più persone, vuoi calcolare le quantità per numeri diversi di porzioni.

Implementa una funzione scaleRecipe che prende due parametri.

  • Un vettore di quantità double necessarie per 2 porzioni.
  • Il numero di porzioni che vuoi cucinare.

La funzione deve restituire un nuovo vettore con le quantità necessarie per il numero di porzioni desiderato. Vuoi però mantenere la ricetta originale. Questo significa che l'argomento quantities non deve essere modificato in questa funzione.

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

5. Sblocca il segreto di famiglia

Ricordi le storie della tua infanzia, i racconti della tua prozia, la custode dei più profondi segreti culinari della famiglia. La leggenda narra che possiede la chiave per sbloccare sapori oltre ogni immaginazione, ma i suoi metodi sono poco convenzionali. Non usa liste né misure: parla sottovoce, rivelando un solo ingrediente alla volta.

Con un misto di entusiasmo e timore, decidi di contattare la tua prozia. Come previsto, risponde in modo criptico, offrendoti un unico ingrediente segreto avvolto nel mistero.

Il contributo della tua prozia aggiunge un livello di complessità, che vuoi usare nella tua ricetta. Implementa un'altra versione della funzione addSecretIngredient che accetta due argomenti: un riferimento a un vettore che rappresenta la tua lista di ingredienti e una singola stringa che rappresenta l'enigmatica offerta della tua prozia.

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

Buona fortuna, coraggioso chef!

Modifica tramite GitHub Il link si apre in una nuova finestra o scheda
C++ Exercism

Vuoi iniziare Maestro di lasagna?

Iscriviti a Exercism per imparare e padroneggiare C++ con 19 concetti100 esercizi e il mentoring di persone reali, tutto gratis.