“
A journey of a thousand miles begins with a single step.
~ Lao Tzu
While one of the aims of this course is to help you develop into an awesome computer scientist, there is also a larger goal. As you learn more about computer science and as you tackle larger and larger problems and programs, we hope that you start to develop stronger and stronger problem-solving skills.
This doesn’t simply mean getting better at finding solutions. It means getting better at engaging in the process of finding solutions. Does that make sense? Maybe not. Let’s take a closer look…
Imagine that you have a very difficult problem to solve. Maybe it’s developing the culminating task in this course, or maybe it’s cooking dinner for your whole family, or maybe it’s improving an aspect of your community or school. Just imagine a very difficult problem.
Rather than simply brainstorming possible ideas and then picking the best one, it might be a better idea to use one of the following problem-solving strategies:
This is a great one to start with because you have probably already been using this problem-solving method in the course. You begin to create a program, you run it, the output generated is different than you expected, so you alter the code and try again. Then you repeat the process.
The trial and error method is great for computer programming, because the cost of each trial (in terms of time and money) is very small. It only takes a few seconds to alter code, and each trial is free to run.
If you were building a bridge, the trial and error method might not be the best. The materials and time it would take for each trial would be large, and if an attempt failed it would be disastrous. (If you want to become a bridge builder, promise not to use this method, or at least let everyone know which bridges you’re building.)
This method is a bit like trial and error. The main difference is that with trial and error you might be writing a whole program, clicking run, and crossing your fingers. With stepwise refinement, you complete the program in a way that gets you closer and closer to the solution. This might involve writing the first part of the program, perfecting it, and then continuing with the rest of the program.
Or, it might involve getting the program to run without worrying about decimal values or the user entering in invalid data. Once you have it running this way, you would then refine it by getting it closer and closer to a perfect program.
This method involves taking a huge task and breaking it up into smaller parts. Imagine that you are writing a program that prompts the user for some data, performs some calculations, and then outputs the data in a user-friendly way. This is a perfect example of an input-processing-output problem.
It would probably be best to divide this program up into the three main parts:
You could start by working on the first part of the program, and once it is working perfectly, move on to the second part, and finally the third.
Now that you’ve had a chance to create some programs, you should take a step back to ask: “What problem-solving strategies are working for me? And which ones are not?”
Consider the way you have solved the problems so far in the course.
In this activity, you are going to get a chance to combine all three of these problem solving techniques (trial and error, stepwise refinement and divide and conquer) as you learn all about modular programming!
Modular programming involves breaking up large programs into smaller steps. In modular programming, the programmer not only imagines different parts of the program, but he or she programs the parts in separate chunks or blocks.
The following video will take you through some of the big picture ideas behind modular programming. Again, don’t worry about the details such as the syntax, where the code goes, etc. Just watch in order to see how the programmer creates a modular program.
Let’s break down some of the components of the video above in order to get a better idea of how to write modular programs.
The following program prompts the user to enter two numbers. It then adds the two numbers and outputs the sum, and also multiples the two numbers and outputs the result.
package temp2;
import java.util.Scanner;
public class Temp2 {
public static void main(String[] args) {
Scanner keyedInput = new Scanner (System.in);
int num1, num2, sum, product;
System.out.println("Enter the first number:");
num1 = keyedInput.nextInt();
System.out.println("Enter the second number:");
num2 = keyedInput.nextInt();
sum = num1 + num2;
product = num1 * num2;
System.out.println("The sum is: " + sum);
System.out.println("The product is: " + product);
}
}
In the programmer’s mind, this program is probably divided into chunks. They are probably thinking:
While the programmer can divide the program up a bit in his or her mind, he or she can also divide it up as it is being programmed, by creating blocks or chunks of code that look after specific components. The blocks or chunks can then be combined, and it results in an efficient, well-organized program.
The programmer can create a main program that will prompt the user for the numbers, but then the adding and multiplying will be done in subroutines.
The following interactive shows you the source code that implements two subroutines to complete the project. Rollover the different parts of the project to learn more.
Now take a closer look at the actual subroutine and its components:
At this point you might be wondering what happens to program control as subroutines are executed. Which line is executed first? Which line is executed second? Which line is executed last?
The following interactive will take you through a few examples that demonstrate what is happening when you use subroutines in your programs:
Remember in the last activity when you talked about parameters and arguments? You use the word parameter when you are writing the definition of the subroutine. You use the word argument when you are talking about the actual method call.
On line 7 above, n1 and n2 are the parameters of type int, written in the definition of the method addAndOut. On line 31, addAndOut is called passing num1 and num2 as arguments to the method.
In the interactive above, you may have noticed how the main program used variables num1 and num2, but the subroutines used variables n1 and n2. What exactly was going on here? At this point you might be wondering what happens to program control as subroutines are executed. Which line is executed first? Which line is executed second? Which line is executed last?
On line 31, the main program passes the values of num1 and num2 to the subroutine as arguments. The subroutine then “takes” these values and stores them temporarily in n1 and n2.
When the subroutine is done executing, and program control goes back to the main program, and n1 and n2 disappear and no longer exist. They are only in existence while the subroutine is being executed.
Your task is to create a program that displays to users a menu that allows them to select from five different mathematical calculations.
Once the user makes a selection, the program then prompts the user for the required data, performs the calculation, and outputs the data in a user-friendly format. The program should then loop back and present the user with the menu again. The menu should also have an option that allows the user to exit the program.
Your program should be written using at least five subroutines, one for each mathematical calculation that you have chosen. The subroutines should be written similar to the addAndOut and multAndOut subroutines above.
The following are some suggested mathematical calculations. See if you can program some of the more complex ones:
Think carefully about what data the subroutines need to accomplish each task. Some of the data will come from the user. Other data might involve using constants.
The addAndOut and multAndOut subroutines that you just created are sometimes referred to as procedures. They perform a task, but they don’t return any data back to the main program, and therefore, they return void.
Functions, however, perform a task and also return data back to the main program. To do so, a return type needs to be specified and a return statement needs to conclude the subroutine.
In Java, you use the word "methods" as an umbrella term to include both procedures and functions.
The following video is the same one that you watched at the beginning of this activity. Rewatch it in order to solidify some of these concepts:
This was done by:
1. specifying the return type in the method definition:
2. including a return statement in the method:
3. having the ability to receive the data in the main program (in this case, the method call was within an output statement so the data that is returned is output immediately to the screen):
Quite often you might not want to output the data that is returned by a method. Instead, you might want to store it within the main program and use it later. In this case, you would need to ensure that you have a variable of the correct type, waiting to store the returned data:
So far, the subroutines that you have created have all used the same type of parameters. As an example, the multTwoNums subroutine required two integers:
It is possible to create procedures or methods that receive more than just two parameters. It is also possible for a procedure or method to receive a variety of parameter types.
The program below prompts users to enter in some information about themselves. The program then uses a procedure to output the data in a user-friendly, well-formatted message:
Notice how there are four different types of parameters that are declared to the subroutine? So when dataOutput is called, the data passed as arguments need to be passed in the same order.
The order is established by the subroutine definition. In this case it’s String, String, integer, double.
Did You Know?
While it is possible for a method to have more than one parameter passed to it, it is not possible for the method to return more than one “piece of data.”
The return statement can only include one variable to be returned to the main program.
Which of the following method definitions is correct?
b. public static int subTwoNums (int num1, int num2)
Which of the following method definitions is correct?
d. public static boolean subTwoNums (String word, double num1, double num2)
Name: secretCalculation
Input/Parameters: integer, integer, double
Output/Returns: double
In the main program, the following code has already been written:
int num1 = 4;
int num2 = 67;
double num3 = 54.87;
double num4;
Which of the following method calls to secretCalculation will work?
d. num4 = secretCalculation (num1, num2, num3);
As you start to create more and more elaborate subroutines, it’s important for you to understand some programming conventions surrounding commenting. While it’s important to comment the lines of code within the subroutine, it is also good practice to provide what is sometimes known as a subroutine header within the program.
A subroutine header is a series of commented lines that explain what the subroutine does. The header also provides information about what parameters are passed to the subroutine, as well as what is returned by the subroutine if it is a method, or whether it is void if nothing is returned.
The following is a good example of a header for the dataOutput procedure:
The following is a good example of a header for the multTwoNums method:
Did You Know?
Did you notice that how the commented lines above, in the subroutine header, do not all begin with the // symbols? Instead the /* and */ symbols are used instead.
These are used to begin and end a multi-line comment. Anything between the two symbols are considered comments in Java, and are skipped by the compiler.
Using meaningful subroutine names also helps make readable code – like in the examples above.
Earlier in this activity, you created a program that performed a variety of math calculations.
The program displayed a menu to the user that allowed him or her to select from five different mathematical calculations. Once the user made a selection, the program then prompted the user for the required data, performed the calculation, and output the data in a user-friendly format. The program then looped back and presented the user with the menu again. The menu also had an option that allowed the user to exit the program.
It is now time to create a similar program with a few important differences:
Think carefully about what parameters the subroutines need to accomplish in each task. Some of the data will come from the user, while other data might involve using constants.