If-statements, Loops, and Functions =================================== If-statements ------------- C++ uses ``if`` and ``else`` to make choices. For example:: cout << "How old are you?"; int age; cin >> age; if (age <= 0) { cout << "That's impossible!\n"; } else if (age < 1) { cout << "You're very young for a user.\n"; } else if (age > 10 && age < 20) { cout << "You're a teenager.\n"; } else { cout << "That's a very interesting age.\n"; } cout << "done\n"; When C++ executes this if-statement, it first checks to see if the expression ``age <= 0`` is true or false. If it's true, it prints the message underneath it, and then immediately jumps out of the if-statement and prints "done". But if it's false, the expression ``(age < 1)`` is checked next. If that's true, the corresponding message is printed, and then C++ immediately jumps out and prints "done". But if it's false, it continues to the next expression. If ``(age > 10 && age < 20)`` is true, then the "teenager" message is printed, and then "done" is printed. Otherwise, if it's false, then the message in the ``else`` block is printed. In this particular if-statement, there are three conditions that are checked. They are checked in the order they occur, and the first one that is true will cause the block of code underneath it to be executed. If none of the expressions are true, then the code block under ``else`` is executed. If-statements can have many different forms. For instance:: if (age < 0) { error("age can't be negative"); } If the code in the body of the if-statement is only a single statement, then the curly braces are not necessary:: if (age < 0) error("age can't be negative"); If it is short enough, the statement can even be written all on one line:: if (age < 0) error("age can't be negative"); Generally, it is wise to always include the curly braces to avoid confusion. For instance, don't be mislead by the indentation in this code:: if (age < 0) cout << "age can't be negative"; cout << "it must be greater than 0"; // misleading indentation! The second ``cout`` statement is *always* executed, no matter the value of ``age``, because it is *not* inside the if-statement. The correct indentation makes this clear:: if (age < 0) cout << "age can't be negative"; cout << "it must be greater than 0"; // better indentation If you always include the curly braces, then this will never be a problem:: if (age < 0) { cout << "age can't be negative"; cout << "it must be greater than 0"; } If the if-statement and its body can fit on a single line, then it's probably okay to leave off the braces. .. note:: C/C++ also provide an if-like structure called a ``switch`` statement. ``switch`` is a popular way to efficiently implement if-statements that use only simple conditions on integers. You can read more about them in our textbook. But be warned: ``switch`` statements do *not* work like any other kind of statement in C/C++, and have a number of annoying limitations and rules that you must keep in mind when using them. You can always easily rewrite a ``switch`` statement as an if-statement, and so we will not be using ``switch`` statements in this course. While Loops ----------- While-loops are one of C++'s basic structures for repeating --- or *iterating* --- a block of code. For example, this program prints the squares of the numbers from 1 to 99:: int main() { int i = 0; while (i < 100) { cout << i << " " << i * i << "\n"; ++i; } } Intuitively, you can think of a while-loop as saying "repeat the following block of code until it's *not* the case that ``i < 100``". Some notation: - ``int i = 0`` is an *initializer* for the loop, i.e. it initializes the loop variable ``i``. - The line ``while (i < 100)`` is called the loop's *header*, and ``i < 100`` is the loop's *condition*. - The code block after the header is called the loop's *body*:: { cout << i << " " << i * i << "\n"; ++i; } The loop body is only executed when the condition is true, when ``i < 100`` is true. After the body executes the flow of control jumps back up to the top of the loop to check the condition again. - The final statement of the loop is ``i++``, and this is informally known as the loop's *increment* statement. This is important because without it we'd have an infinite loop, e.g.:: int i = 0; while (i < 100) { cout << i << " " << i * i << "\n"; } This loop never stops! It's surprisingly easy to forget increment statements. Lets see a few more examples of loops. This loops prints the numbers from 1 *up to* 10:: int i = 1; while (i <= 10) { cout << i << "\n"; ++i; } This loop prints the numbers from 10 *down to* 1:: int i = 10; while (i >= 1) { cout << i << "\n"; --i; } This loop reads in strings from the keyboard, stopping when an end-of-file (EOF) character is encountered (ctrl-d in Linux):: int main() { string word; // initialized to "" while (cin >> word) { cout << word << " " << word.size() << "\n"; } } The expression ``cin >> word`` is both the loop condition *and* the loop increment. When ``cin >> word`` reads a word, it returns ``true`` if it actually did read in a word, and ``false`` if it instead read in the EOF character. It also puts the just-read word into the variable ``word`` (the increment). Combining a condition and increment like this is typical in C++, although it can get confusing especially when you first see it. Generally, we will try to avoid this and keep the increment and condition separate. .. note:: C/C++ also has a loop structure called a do/while loop. The essential difference between a do/while loop and a regular while loop is that the condition of a do/while loop is checked at the *bottom* of the loop after the code in the body has been executed. Thus, a do/while loop's body is always executed at least once, where the code for a while-loop might never be executed (i.e. if the condition is false the first time it is checked). However, do/while loops are rarely useful in practice, and so we will skip them in this course. For Loops --------- For-loops are very similar to while-loops, except they bundle all the loop-control information into the top of the loop. For example, this loop prints the squares of the numbers from 1 to 99:: int main() { for(int i = 0; i < 100; ++i) { cout << i << " " << i * i << "\n"; } } For-loops can look intimidating at first because all the loop-control information is in one place at the top of the loop. In contrast, the while-loop version of this program scatters this information around in three different places. In general, for-loops are preferable to while-loops because they are more organized and easier to understand. In general, for-loops have this structure:: for(initialize; condition; increment) { // ... code body ... } If you were to convert this to a while-loop, it would be this:: { initialize; while (condition) { // ... code body ... increment; } } Notice that the initialization statement is *outside* the loop, and the increment is inside the loop at the bottom of the code body. A for-loop can easily be converted to a while-loop, and so some programmers think of a for-loop as simply `syntactic sugar `_ for a while-loop: there's nothing that you can do with a for-loop that you can't do with a while-loop. Lets write for-loop versions of the while-loops from above. This program prints numbers, and their squares, from 0 up to 99:: for(int i = 0; i < 100; ++i) { cout << i << " " << i * i << "\n"; } This loops prints the numbers from 1 **up to** 10:: for(int i = 1; i <= 10; ++i) { cout << i << "\n"; } This loop prints the numbers from 10 **down to** 1:: for(int i = 10; i >= 1; --i) { cout << i << "\n"; } And finally, this loop reads in strings from the keyboard, stopping when an end-of-file (EOF) character is encountered (ctrl-d in Linux):: for(string word; cin >> word; ) { cout << word << " " << word.size() << "\n"; } Look carefully at the for-loop header: there is nothing after the second ``;``. That's because ``cin >> word`` is both the condition and the increment. Note that the variable ``word`` is *not* usable after the for-loop:: for(string word; cin >> word; ) { cout << word << " " << word.size() << "\n"; } cout << word; // compiler error: word is out of scope When you define a variable inside a for-loop header, it is only usable in the header an in the body of the loop. While this can be annoying in some cases, it is actually a wise pre-caution that can prevent subtle errors. If you really do want to use ``word`` outside of the loop, move the initialization code outside of the for-loop like this:: string word; for(; cin >> word; ) { cout << word << " " << word.size() << "\n"; } cout << word; // okay: word is in scope Finally, occasionally it is useful to create an infinite loop, and C/C++ programmers often uses the following style of for-loop for that:: for(;;) { // infinite loop cout << "happy happy joy joy\n"; } This program runs forever, or until you do something drastic like kill it in the operating system, or turn off the computer. In practice, you can use either while-loops or for-loops. However, we will generally be using for-loops since they are almost always more readable than while-loops. Functions --------- A function is a named block of statements that help us re-use code. For example:: double square(double x) // header { return x * x; // body } This function is named ``square`` and it takes a single ``double`` as input and returns a value of type ``double``. The ``return`` statement tells us what value the function evaluates to when it is called. For example:: double m = square(5); // m is 25 double n = 10 * square(2) + square(3); // n is 49 An expression like ``square(5)`` is a *function call*. When ``square(5)`` is evaluated, the following happens: - A *copy* of the value 5 is passed to ``x`` (the name of the variable in the function header), i.e. ``x`` is assigned a copy of 5. - The expression ``x * x`` is evaluated. Since ``x`` is 5, this is 25. - Finally, the function ends by *returning* the value 25, meaning that the function call is essentially replaced with the value 25. The ``return`` statement is important, and every C++ function that evaluates to something should end with one. Also, when a ``return`` statement is encountered, the function immediately ends. So something like this would *not* work:: double square(double x) { return x * x; cout << "square called\n"; // never executed! } .. note:: C/C++ does *not* have a special operator for squaring values. However, some languages do. For instance, in Python ``x ** 2`` is the square of ``x``. C++ Functions Compared to Mathematical Functions ------------------------------------------------ It's worth taking a moment to compare C++ functions to regular mathematical functions. In mathematics, you define the square function like this: .. math:: f(x) = x^2 Evaluation of a mathematical function and a C++ function works essentially the same, at least on a conceptual level. From math class you know that, say, :math:`2 + 3f(4)` equals :math:`2 + 3 \cdot 4^2 = 50`. Similarly, the C++ expression ``2 + 3 * square(4)`` is also 50. If we were to use C++ terminology, then :math:`x^2` is the return value of :math:`f`. Mathematicians don't bother to write "return" since it is understood to be a return value. Mathematical functions are often short expressions long, and don't include things like variable assignments or print statements. We need ``return`` in C++ to distinguish regular non-returning statements from ``return`` statements. This definition also doesn't say what kind of value :math:`x` is. Instead, we simply know that squaring works with real numbers and so assume :math:`x` is real. A more precise definition of :math:`f` might say that it maps from reals to reals; this is closer to what C++ requires. We also try to give self-descriptive names to C++ functions so that C++ source code is easy to read. In mathematics we use the name :math:`f` because it is short and so easy to write by hand. But in C++, it's more important that a function name give a hint about what it does, and so ``f`` would be a very bad name for a function that squares its input. Finally, we note that C++ functions are far more general than mathematical functions. For instance, there is no mathematical equivalent for a function like this:: void print_hello() { cout << "Hello!\n"; } Mathematics doesn't deal with notions like "printing", or "reading files". Examples of Functions --------------------- Lets see a few more example of C++ functions. **Example.** This function inverts a ``double``:: double invert(double x) { return 1 / x; } A problem with this function is that ``1 / x`` is undefined when ``x`` is 0:: cout << invert(0) << endl; // can't invert 0: 1 / 0 undefined When I run this on my computer it prints ``inf``. But really, we should treat it as an error, and so it is wiser to re-write ``invert`` like this:: double invert(double x) { if (x == 0) error("can't invert 0"); return 1 / x; } Now if you try to call ``invert(0)``, the program is immediately halted with an error message. **Example.** Not all functions return values. For instance:: void print_instructions() { cout << "To play game\n" << "insert quarter\n"; } The return-type of this function is ``void``, meaning it does not evaluate to anything when you call it. Instead it just prints some text to the screen, which is *not* the same as returning the text. You can only call a ``void`` function as a statement:: print_instructions(); // okay string s = print_instructions(); // compiler error! **Example.** Here's a function that can be useful for reading numbers from the user:: double get_double() { cout << "Please enter a double: "; double x; cin >> x; return x; } This function reads a ``double`` from the console and then returns it. You can use it like this:: double a = get_double(); double b = get_double(); cout << "a + b = " << (a + b) << "\n"; This is quite convenient, and you may want to use this function in your own programs. **Example.** A small improvement we can make to ``get_double`` is to allow the programmer using it to supply their own prompt:: double get_double(string prompt) { cout << prompt; double x; cin >> x; return x; } Now you can write this code:: double a = get_double("Please enter a: "); double b = get_double("Please enter b: "); cout << "a + b = " << (a + b) << "\n"; The only problem with this new function is that sometimes we might be happy with the default prompt and not want to bother to supply one. So C++ lets us supply a default value for the prompt:: double get_double(string prompt = "Please enter a double: ") { cout << prompt; double x; cin >> x; return x; } Now supplying a prompt is optional:: double a = get_double("Please enter a: "); double b = get_double(); cout << "a + b = " << (a + b) << "\n"; .. warning:: The string that is being passed to ``get_double`` is being **passed by value**. That means a copy of the string is made, and, if the string is big, that will take extra time and memory. We'll see later how to avoid this problem (without using pointers) by instead passing ``prompt`` as a **constant reference**:: double get_double(const string& prompt = "Please enter a double: ") { cout << prompt; double x; cin >> x; return x; } This style of parameter-passing does *not* make a copy of the passed-in string. Instead, ``get_double`` refers to the actual original copy of the string a read-only way (so ``get_double`` can't change ``prompt`` in any way). We'll learn more about this as the course continues. **Example**. This function restricts a number to a given range:: double constrain(double x, double lo, double hi) { if (x < lo) return lo; if (x > hi) return hi; return x; } For example:: double x = get_double(); x = constrain(x, 1, 10); cout << "x = " << x << "\n"; Or:: double x = constrain(get_double(), 1, 10); cout << "x = " << x << "\n"; Or even:: cout << "x = " << constrain(get_double(), 1, 10) << "\n"; Note that there are a number of other ways to write the ``constrain`` function. For instance, this also works:: double constrain(double x, double lo, double hi) { if (x < lo) { return lo; } else if (x > hi) { return hi; } else { return x; } } Different programmers prefer different versions. The first one is short and relatively easy to read, while the second version seems cluttered with all the braces. However, the second version is more consistent and explicit, and so may be easier to modify in practice. Local and Global Variables -------------------------- You are allowed to define variables inside a function:: double get_double(string prompt = "Please enter a double: ") { cout << prompt; double x; cin >> x; return x; } Here, both ``prompt`` and ``x`` are defined inside ``get_double``. This means that these variables are only usable inside ``get_double``: when ``get_double`` ends both ``prompt`` and ``x`` are automatically deleted. Variables defined inside a function are called **local variables**. Variables declared outside a function are **global variables**. Local variables only exist in the function in which they are defined, which means you cannot reference them outside of they're function. More specifically, all definitions in C++ have a **scope**, i.e. a span of text within the program where it is usable. The scope of local variables is the function they are defined in, while the scope of global variables is the entire program. Since a variable need not be defined at the start of a function, the scope of a local variable might not be the entire function. For example:: double get_double(string prompt = "Please enter a double: ") { cout << prompt; double x; cin >> x; return x; } The scope of ``x`` starts at the second line of this function. If you were to try to access it before the statement ``double x``, you'd get an error, e.g.:: double get_double(string prompt = "Please enter a double: ") { cout << x; // compiler error: x not defined! cout << prompt; double x; cin >> x; return x; } However, it is possible that if ``x`` were also defined as a global variable, then there is no error, e.g.:: double x = 55; // global x double get_double(string prompt = "Please enter a double: ") { cout << x; // okay: prints global x cout << prompt; double x; // local x cin >> x; // local x return x; // local x } Local and global variable names can be quite confusing, and so it is considered good practice to simply not use global variables. .. note:: Some languages, such as Java, do not have global variables at all and so never have this sort of problem. Programming Questions --------------------- #. As of summer 2012, you could `buy Finch robots `_ in bulk according to these prices: - 1 to 4 robots: $99 each - 5 to 19 robots: $94 each - 20 to 49 robots: $89 each - 50 to 99 robots: $84 each - 100 or more robots: $79 each For example, 25 robots would cost $2225, because :math:`25 \times 89 = 2225`. Write a program that asks the user how many robots they want to buy, and then tells them the total cost. If the user enters 0 or fewer robots, then print a friendly message say that they need to buy at least 1 robot.