CSIS 10A Lab 4: Functions Part 1—Random Story Generation (Void Functions) 5 pts An important skill: Offloading Tasks to Functions As we write more complicated programs it becomes necessary to break them into smaller pieces. We can solve the pieces individually and then assemble them into a much larger program. The small programming units you will write in this lab are called Functions, and writing them is one of the most important skills you will develop in this class. What is a function? To put it briefly, a function is a unit of code, enclosed within { }, with a name on top of it, that performs a particular task and can be "called" or used easily in other program statements. This lab explores how to create and use your own functions. We’ll start with simple functions that display messages and slowly build to mathematical functions that perform calculations for us. void Functions 1. Start by dowloading and running Lab4Function1.cpp. Notice that when the program runs, nothing is displayed except "press any key…" However if you look at the code you'll find it has a cout statement in it: cout<<"Once upon a time, "; You might ask yourself, why doesn't that statement execute? 2. The reason the cout statement does not execute is because it is not inside the main program block we've been writing for all our programs. When a program runs, the first line that is executed is the first line inside the main block. Actually, instead of main block it should really be called the main function because that is what main( ) really is: a unit of code, enclosed within { }, with a name on top of it— main. There is another block of code—function—in this program. It's name is opening and it's return-type is void . The return-type means the value that the function delivers back when it finishes executing. In this case, void means there is no value for it to deliver. What is the return-type for function main? Does it look familiar? Function main returns an int when it finishes executing. That int is the number 0 which is shown in the return 0; statement at the bottom. This statement returns a 0 to the operating system when main finishes, which says the program finished with no errors. We will work more with the return statement later in the lab when we create 1 mathematical functions. For this section, all our functions will be void functions that do not need return statements. 3. So…how do we get our program to print something? We need to insert a special statement inside main that invokes, or calls, the function. We call this a function call and it looks like this: opening( ); Put this line right after the open-{ in main, which is right before the system("pause"); statement This is a function call. It is a special statement that tells the computer to jump into a different function block than the one it is currently in. 4. Run the program now and see what happens. You should now see the message. So now, when the computer starts executing main, it sees the function call opening( ); and jumps into the opening function, where it hits the cout statement. When it finishes it jumps back into main to the line after the function call to continue and finish the main function. So now you can see that a function call starts off a controlled jump into another part of the program where a small task can be performed, in this case, displaying the start of a story. This is also very interesting to check out with an interactive debugger. 5. You can re-use your function several times if you like. For example, you can put another function call below the first one in main and get a second copy of the message it displays. Try that now and see if it works: 6. To see if you got the idea, try writing another void function block called noun_phrase( ) and put it between the opening( ) and the main( ) functions. Then add a function call in main to activate your new function. The noun_phrase function should display a message like "a blue goose" or "ten hungry jackals". If it works you will see your new message added to the screen output. 2 7. Add a 3rd function verb( ) to display a verb. This could be any active verb like "walked", "devoured", "smelled", etc… Then add a function call to main to call your new function after the other two have been called. If it works you will see all three messages added to the screen output. such as "Once upon a time, ten hungry jackals devoured" (We will add to this shortly) Of course you might be wondering why can't we just put these cout statements in main? The answer is, you could, however, you will discover why it's better to use functions in a few minutes. Function Parameters Allow Variety Right now your story is the same each time you run it. It would be nice to allow some variety in message selection, so we are going to complicate the situation a little bit by allowing main to give some instructions to the functions it uses. This will allow a choice in output messages. 8. Modify your defnition for opening( ) by adding a variable declaration int n inside the parentheses of the function header (the first line of the function definition above main) and change the code inside the function to use an if statement to choose 1 of 2 statements, depending on the value of n: When you declare(int n) a variable in the function header of a function, it is called a parameter of the function. So variable n is a parameter of function opening( ). A parameter is a special variable that allows information to be passed into the function during the function call. 9. Now modify your main function to put a number (0) in the parentheses of the function call for opening( ): 3 10. The number 0 in the above listing is called an argument to the function call. When the function call for opening( ) is processed in main, the value 0 in parentheses gets copied into the parameter n that you just inserted into the function header. The purpose of n is to receive the argument that you give when calling the function. Run the program now and see if you get the same opening you had before. Then change the argument to 1 and see if you get the other opening. 11. a) Add parameters to the other functions you've created, and a set of alternative noun_phrases and verbs that you will choose between, b) add arguments to your function calls and c) re-run your program several times, changing the arguments to select different lines in your functions. Note: you may use the parameter n for each of your functions. There is no danger of "redeclaring a variable" because each instance of parameter n would belong to a different block of code. The compiler keeps the blocks separate, allowing you to re-use variable names in different functions as you desire. 12. Before continuing, take a moment and increase the number of cout statements in each function to at least 4, using the if/else if/else if/else structure you learned last week to select between them. We are about to discover a way to select the statements randomly, so it's good to have a lot to choose from. Adding Randomness To Your Story Computers are very precise machines, so how can they generate random numbers? It turns out there are precise mathematical formulas that give "apparently" random behavior when they are used over and over again. They are not truly random so they are often called "pseudo-random" numbers. But for our purposes they work just as well as real random numbers. 13. The code for creating a random number is inside the function rand( ) that is part of the standard C++ library (what we get when we say using namespace std). To use the rand( ) function, however, you have to capture the value it returns, so we assign the function call to another variable, like this: int x; x = rand( ); // x holds a random number between +/-2 billion The function rand( ) is an example of a value returning function, which we will write more of in the next lab. Unfortunately, the range of rand( ) is too large for us. We only want a number between 0 and 2 or 3. We are going to use the % (remainder) operator to chop down the random value we get. So the correct expression for a random value between 0 and 3 (4 possible values) is 4 x = rand( )%4; cout<<x; // x holds a random number between 0 and 3 // look at your random number 14. To test these ideas, add statements to generate a random number and display it at the beginning of main: 15. Run the program a few times and you'll probably notice you always get the "same" random number. To randomize the generator you will need to seed the math formula with the system clock of your computer (always starting it off at a different initial value gives unpredictable random numbers). This is done by adding srand(time(NULL)); to the very top of your main function. Note: you only seed the generator once when the program starts running. Do not copy this line anywhere else in the program: 16. Now that you know your random number generator works, you can "pass" random numbers to your functions (and thereby select random story components) by assigning a new random number to variable x before each of your story function calls, and then passing the variable x into the function, like this (also be sure to remove the cout<<x<<endl; statement) 17. Finally, add another call at the end of your story sequence to the noun_phrase( ) function, this way you can write stories such as, "Once upon a time, ten hungry jackals walked a blue goose" or "A long time ago, in a galaxy far, far away, a blue goose walked ten hungry jackals" When you write the code this way, the variable x is now the argument to the function call, and it's value is copied to parameter n inside the function header. 18. Here is what your code in main( ) should look like after you have finished this section: 5 Local Variables: Letting the Functions Generate Random Numbers By now you can probably see the value of functions. By breaking up the story into pieces (functions), you can let each piece handle its part of the job, and then build up an entire story by stringing function calls together in the order you want. You could easily extend your random story by continuing to invoke your noun and verb functions in main (you might need a larger sample of selections to make it interesting) but before we leave this exercise there are two small stylistic modifications we can make in the way the code is organized. The first is to move the random function generators into each function so we don't have to do it in main each time we want another piece of the story. 19. To do this, a) add statements to declare and initialize int n with a random value at the beginning of each of your story functions (just after the open curly brace), b) remove int x and all rand( ) function calls from main, and c) also remove the parameter (int n) from the parentheses in the function headers. Why are we removing the parameters? Well, since we are no longer passing any information into the function from main, we don't need the parameters. However, the use of parameters is an important function concept we'll see again when we write value returning functions. 20. This is how your opening function might look now: 6 21. And this is how main( ) might look: 22. Variables declared with the curly braces are referred to as local variables. They are just like variables you declare in main( ). And because the functions are separate areas of code, the variables you use in main can be re-declared (if you want) in any other function. The point is, when writing a function, you don't have to worry what variables are used in another function. Function Prototypes 23. People sometimes get confused about the overall layout of a program when functions are involved. You have been writing your function definitions before the main function definition. There are three rules about placing function definitions in your program: 1) The function definition must be placed outside of any other function definition (this means you can't just create a new function in the middle of main) 2) The function must be declared before any function calls to it (this is why the definitions have been written before the function calls in main-so the compiler understands what is meant when it sees the function call) 3) An alternative way of placing function definitions is to move the definition after main and leave only a prototype or signature for the function (a function declaration) before main. (the prototype is like a declaration about the function and gives enough info for the compiler to process the function call.) This is what the program layout would look like under rule 3): 7 24. Finish up this part of the lab by making your code follow rule 3). That is, move all the definitions after main, then copy the function header of each function back to the space before main, and follow it with a ; (that's a prototype). Check the program still works after completing this step. For now, rule 3 is optional but you should be aware of this structural choice when you read the textbook. 25. When you are finished, make sure your name is on top of your code and print it out. This is only the first part of Assignment 4. You can now learn about value returning functions by completing the exercises in Lab4function2.cpp. Part 2—Value-Returning Functions 5 pts Math Functions 8 In addition to generating random numbers, there are a number of math functions provided by C++, but to use them, you need to include the <cmath> library. The math functions generally take one or two parameters and return the results of a computation so you can use them in subsequent calculations. To experiment with some of these functions, download the file Lab4function2.cpp and open it. 26. For example, you might want to compute the value of 24 – that is, the number 2 raised to the 4th power. The function pow( ) will do this for you. It takes two arguments, the first one being the base number, and the second one being the exponent. The function call would look like this pow(2.0, 4) ; // calculate 2 raised to the 4th power 27. Problem 1: Type the previous statement into the problem 1 area of the file, make sure the section is activated and run the program. You'll notice you got an error because the library <cmath> needs to be included at the top of the file. You might be wondering, what is inside the <cmath> library? The answer is, the function definitions for all the math functions, including pow( ) Add this statement now right after #include <iostream> : #include <cmath> 28. Now run the program again. Do you see any result? The computation was performed, however there is no mechanism to hold on to the result. With a value-returning function, we need to either store the answer in a variable, or display the answer to the screen using cout. We'll try both of these methods now. 29. The easiest way to see the result of your function call is to modify the line containing it so it reads: cout<<pow(2.0, 4) <<endl; 30. You can even put a descriptive message in front of it, such as cout<<" pow(2.0, 4) = "<<pow(2.0, 4) <<endl; 31. Try this and observe the result of the calculation. Is it what you expect? 32. Another way to capture the results of a function call is to assign the result to a variable. For Example, float z; z = pow(2.0, 4); cout<<" pow(2.0, 4) = "<< z <<endl; 33. Add this code beneath your previous statements and run it to see if it works the same. 34. Your program would be more interesting if you could input a value and compute it's 4th power. Declare a second variable x as a float, and after your previous statements, ask for a value, input it, then show it's 4th power. HINT: replace 2.0 in the above example with the variable x. 35. Problem 2: There are many useful math functions available in <cmath> (see table below). Experiment with a few of these, in particular fabs ( ) -- which calculates the absolute value of a number, and sqrt( ) – which calculates the square root of a number. Write a short program that inputs a float value into a variable and shows the square root, absolute value and one more function of that value. Test your program with positive, negative, and decimal values. 9 Writing your Own Value Returning Functions The math library has many valuable functions already defined. However, there are many calculations you might need to do that are not included in the math library. In this case, you would need to write your own value returning function. A value returning function is very similar to a void function with only two small changes: 1) Instead of void before the function name, we put the data type of the return value. 2) At the end of the function definition, we put a return statement. 36. You probably still remember the problem from assignment 2 where you convert a number of coins into their equivalent dollar value. The solution was something like: 37. We are now going to replace the fifth statement dollars= …. with a call to a function that we define ourselves. In designing the function, there are a few steps we must go through. The first step is to determine what the name of the function should be. We decide to call this function dollarValue( ) because it converts coins into their equivalent dollar value. We then need to determine what type of parameters the function needs. In order to calculate a dollar value, the function will need to accept the quantity of nickels and dimes to convert.These numbers are naturally represented by the int data type. Finally, we need to determine the return type of the function. This function will return a dollar value, so we will use a return type of float to represent decimal values. We therefore can write the function header for our new function as follows: float dollarValue( int nk, int dm) 10 Note that we have used nk and dm instead of nickels and dimes to highlight the separation between arguments and parameters. The parameters (nk and dm) will receive the value of the arguments (nickels and dimes) to the function call. The function body will then take the parameters and use them to calculate the resulting dollar value, and then return it. We decide to use a local variable called value to store the result so we can return it in the last line. The name of the local variable is not important as long as you don't use the same name as the function (in this case, dollarValue would be a bad name for your local variable). Here is what the function definition for dollarValue looks like: 38. Problem 3: Type in the definition for dollarValue( ) above main( ). Then activate the problem 3 section in main. Run the program once, then replace the dollars = …. statement with a function call to your new function: dollars = dollarValue( nickels, dimes); Run the program again to make sure it still works, typing in various values for nickels and dimes. It's important to remember that the variable names of the arguments can be different from the variable names of the parameters. Remember, the parameter is the variable inside the function header that receives the value of the argument that is given during the function call. To make sure you understand, please label the arguments in the line given under step 38. And label the parameters in the code given under step 37. A) First. change the order of the arguments in the function call and see how that changes the result. You will find that you have inadvertently given nickels to the dime parameter and vice versa, resulting in an incorrect calculation. B) Now try replacing the arguments in your function call with actual numbers, such as dollars = dollarValue( 4, 3) ; C) Now try changing the name of the coin variables throughout the code under problem 3 (in main) to nk, and dm and also use them as your arguments (replacing the 4 and 3) and verify that the program still works. After these steps, you should understand that the name of the parameter is independent from the name of the argument. Whether we give the dollarValue function nickels and dimes, nk and dm or 4 and 3, the parameters in the definition simply receive the values of any of these sets of arguments. 11 39. Problem 4: to see if you've understood the previous exercise, you will now get a chance to write your own function definition and use it in a function call that you create yourself. Deactivate the problem 3 section and activate the problem 4 section. Run the program and notice how it works. What are the inputs? (These will be the parameters of the function.) The program asks for three test scores and computes the average by adding them together and dividing by 3.0. You're now going to design and use a function to perform this calculation. Remember, the steps for designing a function are: 1) decide on the name of the function (just use avg3 for the name) 2) determine the number of parameters and their type: __________________________________________ 3) determine the return-type of the function: __________________________________________ 4) Now write the function header: __________________________________________ 5) Now write the entire function, including it's body, above main and below the definition for dollarValue 6) Replace the formula line in the problem 4 code area with a function call 40. Problem 5: Some calculations are more complicated than others. Remember the paycheck calculation from last week? That calculation needed an if statement to choose one of two possible formulas. We can move the entire if/else statement into our own payCheck function. This makes it very easy if we need to calculate paycheck at several points in our program. First run problem 5 as it is currently written, then follow the same steps for problem 4 here: 1) decide on the name of the function (use payCheck for the name) 2) determine the number of parameters and their type: __________________________________________ 3) determine the return-type of the function: __________________________________________ 4) Now write the function header: __________________________________________ 5) Now write the entire function, including it's body, above main and below the definition for avg3. 6) Replace the formula line in the problem 5 code area with a function call 41. Functions are very flexible in working with different data types. For example, here is a function definition for a function size2inches that converts a pant size of S, M or L, into an equivalent waist size in inches: 12 Notice the type of the argument is char, while the return type is int. Here are some statements that would go in main to use the function: Notice how the argument size is declared as a char, so the data type of the argument matches the data type of the parameter. To remember this connection it is helpful to draw a line connecting the argument with the parameter. You are encouraged to do that now. The same connection holds between the return type of a funcition and any variable that receives the result of the calculation. A function that went the other way, converting a waist measurement into a letter size would have a returntype of char and a parameter of type int. In other words, The function header would look something like this: 42. Problem 6: using the above ideas, create a function that converts a float test score into a letter grade and use that function to complete the code under problem 6 in main. 43. Problem 7: In problem 4 you calculated the average of three test scores, and in problem 6, you calculated the grade associated with an average score. Take this one step further and go back to modify problem 4 so that after calculating the average score, you then convert that score to a letter grade, and show the resulting grade. Just use the function you created in problem 6. No new function definition is required. 44. One last function form we're going to look at is a function that returns a bool result. We may not have covered the bool data type yet, but it is a type that represents true or false values. You can declare a variable of type bool, store a value in it, and use it in an if-statement to determine which statement to execute. For example, 13 bool test; test = true; if (test) cout<<"test is true"<<endl; else cout<<"test is false"<<endl; Why this is useful is shown in the following function, which returns a true or false value depending on the value of n. If n is between 0 and 100 it returns true, Otherwise, it returns false. Bool functions are great for hiding very complicated logical expressions in an easy to understand function call. The name of a bool function usually begins with the word "is" because the function itself is answering a yes/no question, for instance, whether the argument in this case is a valid number. To use a bool function in a program, you can just insert a function call to it inside the parentheses of an if or if-else conditional statement: 45. Problem 8: Finish this lab by writing a bool function that determines whether an age is in the range for a teenager. Insert your function call at the indicated place under Problem 8 in main. We have come a long way in gaining experience writing and using functions. Functions form the basis of much of what goes on in programming today. Hopefully you now have a sense of what functions are and how they can help you solve more complex problems. Print out Lab4Function1.cpp and Lab4Function2.cpp and any challenge problem you wish to complete. See the assignment handout for more information on challenge problems. 14