Question
One of the classic unsolved problems in cooking is scaling recipes so that you make a reasonable amount of food. Cookbook authors seem to enjoy
One of the classic unsolved problems in cooking is scaling recipes so that you make a reasonable amount of food. Cookbook authors seem to enjoy providing recipes that serve improbable numbers of people. The problem is compounded because no one can remember how to convert among all those wacky English measurements (just how many teaspoons are in a ounce anyway?) For this assignment, you are to write a program that will do the necessary recipe re-sizing automagically. In addition to making your life in the kitchen easier, this assignment is intended to give you practice with a project that involves multiple classes (without inheritance).
This project will depend heavily on your Fraction and MyString classes from CS 10B. If you haven't completed these, email me.
The project will be written in four phases. You should test each phase carefully before moving on to the next phase.
Phase 1: The Measure Class
The Measure class consists of a Fractional amount and an associated English unit (e.g., "3/4 tsp"). A Measure object can be scaled by multiplying by another Fraction. To handle this scaling, it must include an overloaded *= operator. The Measure class is responsible for converting the amount to the most "reasonable" unit, which is the largest unit where the amount is greater than or equal to 1 if such a unit exists. Otherwise, the unit should be the smallest known unit. For example, 3 pint should be printed as 1+1/2 qt, 1/2 gal should be printed as 2 qt (not 4 pint), and 1/3 dram remains 1/3 dram.
The class will also have overloaded insertion and extraction operators.
Rather than writing a bunch of special conditionals, we will store the conversion factors in a table (array) so you can convert up and down as needed in the Measure object. Since this table is only used by the Measure class and can be shared among all Measure objects, this is a good opportunity for use of static data. By the way, the conversions are:
4/3 dram = 1 tsp 3 tsp = 1 tbsp 2 tbsp = 1 oz 8 oz = 1 cup 2 cup = 1 pint 2 pint = 1 qt 4 qt = 1 gal 2 gal = 1 peck 4 peck = 1 bushel 55/7 bushel = 1 barrel 6000 barrel = 1 acre_ft
Here is my Measure class declaration. There is some stuff going on with constants and "static" that you may not have seen before. Basically, you shouldn't simply declare a const in a class. You have to declare it as a static const. This is because you don't want a separate LARGEST_UNIT (for example) constant for each Measure object, you just want a single LARGEST_UNIT constant that any object in the Measure class can access. This is done with the static keyword. With the arrays, you then have to initialize the array OUTSIDE of the class declaration, as you can see below.
class Measure { public: friend ostream& operator<<(ostream& out, const Measure &printMe); friend istream& operator>>(istream& in, Measure &readMe); Measure operator*=(const Fraction &right); private: void simplify(); static const int LARGEST_UNIT = 11; static const int SMALLEST_UNIT = 0; // these two constants: we'll be using a system where // dram = 0, tsp = 1, tbsp = 2, and so on. So the // smallest unit (dram) is represented by 0, and the // largest unit (acre_ft) is represented by 11. static const int NUM_UNITS = 12; static const Fraction conversionTable[NUM_UNITS]; static const MyString unitStrings[NUM_UNITS]; Fraction amount; int unit; //if unit = 0, the unit is dram. //if unit = 1, the unit is tsp, and so on. }; const MyString Measure::unitStrings[] = {"dram", "tsp", "tbsp", "oz", "cup", "pint", "qt", "gal", "peck", "bushel", "barrel", "acre_ft"}; const Fraction Measure::conversionTable[] = {Fraction(4,3), 3, 2, 8, 2, 2, 4, 2, 4, Fraction(55,7), 6000}; // these two const definitions should go in the implementation file, // not in the specification file.
Here is my simplify function, complete except that 2 conditions and 2 statements have been removed. It makes sure that the units meet the requirements given in the assignment, that is, the amount should be made as small as possible but never less than 1 (unless of course the unit is already "dram". In that case, the amount will of course have to be less than 1).
void Measure::simplify() { // make the amount bigger if it is less than 1, unless the unit is already //"dram" (in which case the amount can't be made any bigger). while () { unit--; } //make the amount smaller if it can be made smaller without making it //less than 1, unless the unit is already acre_ft (in which case the //amount can't be made any smaller) while ( ) { unit++; } }
In your functions for the Measure class you will want to access some of the constants declared in the class declaration. Keep in mind that if you are defining a member function you can use them freely without any special syntax, but if you are defining a friend function you will have to specify when you use them that they are from the Measure class like this: Measure::SMALLEST_UNIT or Measure::LARGEST_UNIT.
Of the three remaining functions in the Measure class (besides simplify), one of mine is 2 lines, one is 3 lines, and one is 8 lines. When counting lines I don't count declarations or blank lines. I'm telling you this not because you should try to make yours exactly like mine, but so that you won't go too far off in the wrong direction. For example if you are writing your insertion operator and you are up to 12 lines and still going strong, you know something is wrong!
No documentation is required for the Measure class.
Phase 2: The Ingredient Class
An Ingredient is simply a Measure and Ingredient name (stored as a MyString). The Ingredient class will include a *= operator to scale Ingredients, along with extraction and insertion operators. Each of its 3 member functions should be about 2 or 3 lines of code.
No documentation is required for the Ingredient class.
Phase 3: The recipe Class
The recipe class will consist of a name, an array of Ingredients, the instructions, and other data members. Both the name and the instructions are stored as MyString objects. You may not place a limit on the number of Ingredients in the recipe. Rather than use an array of MyStrings for the instructions, use the concatenate feature of MyStrings to combine the instruction lines into one MyString object as you read them from the file. Concatenate newlines in between the lines to retain the original formatting. The original formatting of the instructions must be retained!
The recipe class will also include a * operator. The * operator doesn't exactly multiply. Rather, it returns a recipe which is equal to the original left operand (a Recipe object) EXCEPT that the number of servings is equal to the right operand (a Fraction object) and the Ingredients in the recipe are scaled appropriately.
The Recipe class needs to be a full fledged class with full documentation, constructors, good memory management, and the big-3.
A set of recipes is provided (data). You may assume that the data file is correctly formatted. Each recipe looks something like this:
Banana Split Sarcasm Surprise 1 servings 8 ingredients 4 gal chocolate ice cream 3 gal butterscotch syrup 12 pint sliced bananas 3+1/3 cup nuts 5 peck tofu bricks 1/10 bushel whipped cream 1+1/5 qt rainbow sprinkles 1 cup maraschino cherries Place tofu in trough. Pile ice cream on tofu. Smother in syrup. Garnish with other ingredients. Top with cherries and sprinkles.
In general, the Recipe format is as follows:
- A line indicating the name of the recipe
- A line indicating how many servings the recipe makes
- A line indicating how many ingredients are in the recipe
- A line for each ingredient
- A blank line
- One or more lines of recipe instructions
- A blank line at the end to mark the end of the instructions
Here is the extraction operator for the Recipe class. The Recipe class will have 8 functions including input, output, and *. There should not be a / operator.
istream& operator>>(istream &in, Recipe &readMe) { MyString temp; readMe.name.read(in, ' '); in >> readMe.servings; in.ignore(10000,' '); in >> readMe.numIngredients; delete [] readMe.ingredients; readMe.ingredients = new Ingredient[readMe.numIngredients]; in.ignore(10000,' '); for (int count = 0; count < readMe.numIngredients; count++){ in >> readMe.ingredients[count]; } in.ignore(10000,' '); readMe.instructions = ""; temp.read(in,' '); while (temp != "") { readMe.instructions = readMe.instructions + temp + " "; temp.read(in,' '); } return in; }
Phase 4: The Recipe Program
Your program should first read the list of recipes from the input file into an array of Recipe objects. To keep things simple, you may assume that there will be no more than 100 recipes (so you can use a non-dynamic array in your client). Your program should then enter a loop where a list of choices is printed, the user is asked to enter a recipe number and a number of servings, and then the recipe is converted appropriately and printed out. If the user requests 0 servings, the recipe should not be printed. After the user types a negative number to quit, the program should print out the recipes in their original form. They should be modified so that the units are correct, but the number of servings should match the input file. Please follow the output given in the sample output exactly.
After the user types a negative number to quit, the program should print out the four recipes in their original form to show that your multiplication operator does not change the recipes. (By "original form" I mean that the number of servings has not changed from the value that appears in the input file. The Measures will, of course, be simplified.) You can do this by adding the following code to the very bottom of your main function (where "numRecipes" is the number of recipes that were read from the file):
for (int i = 0; i < numRecipes; i++){ cout << recipeList[count]; }
Data
Banana Split Sarcasm Surprise 1 servings 8 ingredients 4 gal chocolate ice cream 3 gal butterscotch syrup 12 pint sliced bananas 3+1/3 cup nuts 5 peck tofu bricks 1/10 bushel whipped cream 1+1/5 qt rainbow sprinkles 1 cup maraschino cherries Place tofu in trough. Pile ice cream on tofu. Smother in syrup. Garnish with other ingredients. Top with cherries and sprinkles. Moon Quake Shake 6 servings 4 ingredients 1/2 qt Bacardi Amber 1/2 qt Kahlua 3 tbsp lemon juice 2+1/2 cup crushed ice. Blend thoroughly. Serve in a shot glass. Lots O Cookies (Low Sodium) 10000 servings 5 ingredients 4000 gal milk 50 barrel flour 2300 cup chocolate chips 100000 dram vanilla 1 tsp salt Bake. Eat. CIS10B "Vita-Pak" Interesting Program Nutritional Supplement 1 serving 3 ingredients 1/2 qt Capt'n Crunch with Crunch Berries 1 acre_ft Banana Split Sarcasm Surprise 331/17 acre_ft of Jolt Cola (in handy I.V. packs) Attach I.V. to arm. Eat BSSS. Sit in front of computer and throw crunch berries at lab assistants. Wait for office hours.
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started