.. highlight:: c++ Solutions to Final Exam Practice Questions ========================================== Vectors and Strings ------------------- #. Write a boolean function called ``all_same(v)``, where ``v`` is a ``vector``, that returns ``true`` if all the elements in ``v`` are the same, and ``false`` otherwise. If ``v`` is empty, then return ``true``. For example: ``all_same({"cat", "cat", "cat"})`` returns ``true`` ``all_same({"mustard"})`` returns ``true`` ``all_same({"cat", "cat", "dog"})`` returns ``false`` ``all_same({})`` returns ``true`` Sample solution:: bool all_same(const vector& v) { if (v.size() < 2) return true; // at this point we know v must have at least // 2 elements for(int i = 1; i < v.size(); ++i) { // note i starts at 1 if (v[i] != v[0]) { return false; } } return true; } #. Write a ``void`` function called ``constrain(v)``, where ``v`` is a ``vector``, that modifies the elements of ``v`` as follows: - If the element is less than 0, replace it with 0. - If the element is greater than 100, replace it with 100. - Otherwise, don't change the element. For example:: vector a = {6, -10, 0, 100, 44, 101}; constrain(a); // now a == {6, 0, 0, 100, 44, 100} Sample solution:: void constrain(vector& v) { for(int i = 0; i < v.size(); ++i) { if (v[i] < 0) { v[i] = 0; } else if (v[i] > 100) { v[i] = 100; } } } #. Write a function ``just_digits(s)`` that returns a string that is the same as ``s`` except all non-digit characters have been removed. For example: - ``just_digits("Route 7.2, highway56")`` returns ``"7256"`` - ``just_digits(" moose")`` returns ``""`` Sample solution:: bool is_digit(char c) { return '0' <= c && c <= '9'; } string just_digits(const string& s) { string result; for(char c : s) { if (is_digit(c)) { result += c; } } return result; } RGB Structures -------------- RGB color is a popular way to represent colors. In RGB, a color is represented as a mixture of red (r), green (g), and blue (b). Each RGB color has the form (r, g, b), where r, g, and b are integers. We will refer to (r, g, b) as an *RGB triple*, or just a *triple* for short. For example, the triple (255, 140, 0) is dark orange, and (147, 112, 219) is medium purple. A triple (r, g, b) is valid if each of r, g, and b is greater than, or equal to 0, and less than, or equal to 255. Otherwise, the triple is invalid. So, for example, (65, 180, 102) is a valid RGB triple, but (200, 180, 256) is invalid. #. Write the definition for a C++ ``struct`` that represents an RGB triple. Give it a short and self-descriptive name. :: struct RGBcolor { int r, g, b; }; #. Write a boolean function that takes one RGB color ``struct`` (that you defined in the previous question) as input, and returns ``true`` if it is valid, and ``false`` otherwise. :: bool is_valid(const RGBcolor& c) { return 0 <= c.r && c.r <= 255 && 0 <= c.g && c.g <= 255 && 0 <= c.b && c.b <= 255; } #. The RGB triples (a, b, c) and (r, s, t) are equal if a equals c, b equals s, and c equals t. Write a boolean function that takes two RGB color structs as input and returns ``true`` if they are equal and ``false`` otherwise. If one, or both, of the passed-in colors are invalid, then your function should call the ``error`` function instead of returning a value. :: bool equals(const RGBcolor& a, const RGBcolor& b) { if (!is_valid(a) || !is_valid(b)) error("invalid color"); return (a.r == b.r) && (a.g == b.g) && (a.b == b.b); } #. The perceived brightness of the (r, g, b) triple can be calculated with this formula:: brightness = (299 * r + 587 * g + 114 * b) / 1000 Write a function that returns, as a ``double``, the perceived brightness of the an RGB color struct. :: double brightness(const RGBcolor& c) { return (299.0 * c.r + 587.0 * c.g + 114.0 * c.b) / 1000.0; } #. Recall that the standard function ``rand()`` returns a random integer that is greater than, or equal to 0, and less than, or equal to, some very large positive ``int`` called ``RAND_MAX``. Assume that ``rand()`` has been included in your code so that it is available to be used. Write a function that takes no inputs and uses ``rand()`` to return a randomly chosen RGB color struct. For example, you could use it like this:: RGBcolor fill = rand_color(); // fill is set to some random RGB color RGBcolor stroke = rand_color(); // stroke is set to some random RGB color All RGB colors should have the same chance of being returned, and an invalid color should never be returned. :: RGBcolor rand_color() { return RGBcolor{rand() % 256, rand() % 256, rand() % 256}; } Enumerated Types: Days of the Week ---------------------------------- #. Create a C++ enumerated type (use ``enum class``, *not* the old-style ``enum``) to create a new data type called ``Day`` that can be used like this:: Day yesterday = Day::Thursday; Day today = Day::Friday; Day tomorrow = Day::Saturday; Sample solution:: enum class Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }; #. Write a boolean function called ``is_weekend(d)`` that returns ``true`` if ``d`` (which is of type ``Day``) is Saturday or Sunday, and ``false`` otherwise. Sample solution:: bool is_weekend(const Day& d) { return d == Day::Saturday || d == Day::Sunday; } #. Write a boolean function called ``is_weekday(d)`` that returns ``true`` if ``d`` (which is of type ``Day``) is one of the five weekdays from Monday to Friday, and ``false`` otherwise. Sample solution:: bool is_weekday(const Day& d) { return !is_weekend(d); } #. Write a function called ``to_string(d)`` that returns a string representation of ``d``. For example:: cout << to_string(Day::Wednesday) // prints "Wednesday" << to_string(Day::Friday); // prints "Friday" Sample solution:: const vector days = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" }; string to_string(const Day& d) { return days[int(d)]; } #. Write a function called ``next_day`` that takes a single ``Day`` object as input and returns the day that comes after it. For example, ``next_day(Day::Sunday)`` should return ``Day::Monday``. Implement it using a ``switch`` statement. Sample solution:: Day next_day(const Day& d) { switch(d) { case Day::Sunday: return Day::Monday; case Day::Monday: return Day::Tuesday; case Day::Tuesday: return Day::Wednesday; case Day::Wednesday: return Day::Thursday; case Day::Thursday: return Day::Friday; case Day::Friday: return Day::Saturday; case Day::Saturday: return Day::Sunday; } cmpt::error("invalid day!"); return Day::Sunday; // to satisfy compiler } Summing Numbers in a File ------------------------- #. Suppose that the file ``data.txt`` contains 0, or more, `double`\ s. Write a function that efficiently prints the sum of all the *positive* numbers in ``data.txt``; negative numbers should be ignored. You can assume that ``data.txt`` only contains valid double numbers. For example, suppose ``data.txt`` contains these numbers:: -8.1 2.2 1.0 0.0 -233.22 -92.2201 Then your program should print 3.2. Your program should read and process files in the same style as discussed in the lectures and textbook (i.e. C++-style file handling). Write the necessary ``#include`` statements, and put your program entirely inside ``main``. Sample solution:: #include #include int main() { ifstream fin("data.txt"); double x; double sum = 0.0; while (fin >> x) { if (x > 0) { sum += x; } } cout << sum << "\n"; } Strings and Characters ---------------------- #. Write a function called ``is_upper(c)`` that returns ``true`` if the character ``c`` is one of the uppercase letters ``'A'`` to ``'Z'``, and ``false`` otherwise. Sample solution:: bool is_upper(char c) { return c >= 'A' && c <= 'Z'; } #. Write a function called ``to_lower(c)`` that returns the lowercase version of ``c`` if it's an uppercase letter, e.g. ``to_lower('E')`` returns ``'e'``. If ``c`` is not an uppercase letter, then ``c`` is returned unchanged. Sample solution:: char to_lower(char c) { if (is_upper(c)) { return c + ('a' - 'A'); } else { return c; } } #. Write a function called ``to_lower(s)`` that returns a new string that is the same as ``s``, except all lowercase letters in ``s`` have been converted to uppercase. ``s`` itself should not be changed. Sample solution:: string to_lower(string s) { for(int i = 0; i < s.size(); ++i) { s[i] = to_lower(s[i]); } return s; } #. Write a function called ``is_vowel(c)`` that returns ``true`` if the character ``c`` is a vowel, and ``false`` otherwise. The vowels are *a*, *e*, *i*, *o*, and *u*, both uppercase and lowercase. Sample solution:: bool is_vowel(char c) { c = to_lower(c); return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u'; } #. Write a function called ``first_vowel_loc(s)`` that returns the index location of the first vowel in string ``s``. If ``s`` is empty, or has no vowels, then return -1. For example ``first_vowel_loc("sprayer")`` returns 3, and ``first_vowel_loc("MBB")`` returns -1. Sample solution:: int first_vowel_loc(const string& s) { for(int i = 0; i < s.size(); ++i) { if (is_vowel(s[i])) { return i; } } return -1; } #. Write a function called ``swap_heads(s, t)`` that returns a single string that consists of ``s``, a space, and then ``t``, except leading consonants of ``s`` and ``t`` have been swapped. If either ``s``, or ``t``, or both, start with a vowel (or are empty), then return ``s`` and ``t`` with a space between them. For example: - ``swap_heads("Donald", "Trump")`` returns ``Tronald Dump`` - ``swap_heads("Bill", "Gates")`` returns ``Gill Bates`` - ``swap_heads("Emily", "Carr")`` returns ``Emily Carr`` Sample solution:: string swap_heads(const string& s, const string& t) { int i = first_vowel_loc(s); int j = first_vowel_loc(t); if (i < 1 || j < 1) { return s + " " + t; // no change } else { string s_head = s.substr(0, i); string s_body = s.substr(i); string t_head = t.substr(0, j); string t_body = t.substr(j); return t_head + s_body + ' ' + s_head + t_body; } } Randomness ---------- #. Give a small but complete program that shows how to call and use the standard ``rand()`` function. What possible values could ``rand()`` return? Sample solution:: #include #include using namespace std; int main() { cout << "Here are three random numbers from 0 to " << RAND_MAX << ":\n"; cout << rand() << "\n"; cout << rand() << "\n"; cout << rand() << "\n"; } // rand() returns a value from 0 to RAND_MAX, i.e. // 0 <= rand() <= RAND_MAX #. If you don't first call the ``srand`` function, ``rand()`` will always return the same sequence of numbers when you call it. a) Show how to use ``srand`` to initialize the random number generator using the current time as the seed. Sample solution:: #include #include #include using namespace std; int main() { srand(time(NULL)); // seed random number generator with current time cout << "Here are three random numbers from 0 to " << RAND_MAX << ":\n"; cout << rand() << "\n"; cout << rand() << "\n"; cout << rand() << "\n"; } b) Why might it sometimes be useful to give ``srand`` a fixed value, such as 15? Sample solution: When debugging a non-working program that uses ``rand()``, it can be useful to call ``srand`` with a fixed value so that you get the exact same behavior every time you run the program. Another possible use of a fixed seed is in video games that use randomness to generate things in the game. For example, in Minecraft, two different people can generate exactly the same world if they use the same seed. c) Suggest some other ways to get an initial random seed value for ``srand``. Sample solution: There are many things you could try, e.g.: - Ask the user to enter some random letters, and make the seed the sum of their ASCII values. - If there's a camera attached, take a picture and use, say, the average brightness of all the pixels as the random seed. - Create a special dice-rolling robot that rolls a bunch of real dice, and then use the numbers rolled as the seed. In practice, these may not be good or practical ways to generate random seeds. A poor random seed could result in less than random behaviour. This could range from annoying (e.g. a game is less random than it ought to be), to disastrous (e.g. a program that suggests passwords should not be predictable). #. Write a new function called ``rand(n)`` that returns a random ``int`` from 0 up to, but including, the ``int`` ``n``. If ``n`` is less than 1, then throw an error using ``cmpt::error``. Sample solution:: int rand(int n) { if (n < 1) cmpt::error("n must be >= 1"); return rand() % n; } #. Write a new function called ``rand(lo, hi)`` that returns a random ``int`` that is greater than, or equal to, ``lo``, and less than, or equal to, ``hi``. If ``lo`` is greater than ``hi``, then throw an error using ``cmpt::error``. Be careful with the arithmetic here! Sample solution:: int rand(int lo, int hi) { if (lo > hi) cmpt::error("lo must be <= hi"); int n = hi - lo + 1; // + 1 because both lo and hi could be returned return lo + rand() % n; }