Defining and Calling Subroutines
A formal parameter is a variable declared in a function definition and is used in function definitions.
Why Programs Reuse Code
Subroutines, the reusable blocks of code that perform a task, often need outside information to do their work. Parameters are the mechanism that carries that information in. This article works from one line of source code, a call to a subroutine named doTask, and uses it to separate the two kinds of parameter you will meet throughout this topic.
Imagine a program that needs the same job done in three different places. You could copy the same lines three times, or write those lines once and let the rest of the program use them whenever they are needed. A subroutine is the second option: a block of code that can be called multiple times from different parts of a program.
Before reading on, pause and ask yourself: if a program needs the same calculation or the same printed output in several places, would you rather keep the instructions in one place or in every place they are needed? Hold that question in mind, because the main idea of subroutines is an answer to it.
A Subroutine Runs Each Time It Is Called
The important word in the definition is called. A subroutine does not run just because it exists in a program. It runs when the program reaches a point where it calls it. Because the same block can be called more than once, a single piece of code can serve many moments in one run of the program.
The diagram shows one subroutine definition serving two call sites. The definition exists once. Each call is a separate moment in the run, and each moment can supply different values. The source states that a subroutine runs when the program calls it, which is why writing it once lets the program reuse it at many points. Once the subroutine finishes, control comes back to the program at the point of the call, so the main program can carry on with the next line after each call.
Reading the Call doTask(17, Math.sqrt(z+1), z >= 10)
An actual parameter is a value or expression passed to a function when it is called, and it appears inside the parentheses of the call. In the call doTask(17, Math.sqrt(z+1), z >= 10), there are three actual parameters. The first is a plain value, 17. The second is an expression, Math.sqrt(z+1). The third is a comparison, z >= 10. Each is supplied in a fixed position, and the order of the list matters.
The call supplies its three actual parameters in a fixed order, and the subroutine receives them in the order they appear in the call. The formal parameter slots in the diagram are generic positions. The source pack shows the call to doTask but not its definition, so the names of doTask's formal parameters are not given here. What you can rely on is their role: each formal parameter is a placeholder inside the definition that stands for a value supplied later by whoever calls the subroutine.
Formal Parameters Versus Actual Parameters
A formal parameter is a variable declared in a function definition. It is a placeholder that the body of the definition uses. An actual parameter is a value or expression passed to the function when it is called. The two terms name the same mechanism from opposite ends: one is written in the definition, the other is written in the call.
| Feature | Formal parameter | Actual parameter |
|---|---|---|
| Where it appears | In the function definition | Inside the parentheses of a function call |
| What it is | A variable declared as a placeholder | A value or expression supplied at the call |
| Role | Stands for a value supplied later | Provides the value that fills the placeholder |
| In doTask(17, Math.sqrt(z+1), z >= 10) | Not shown in the source pack | 17, Math.sqrt(z+1), and z >= 10 |
Formal and actual parameters compared by where they appear and what they do
How Each Actual Parameter Gets Its Value
An actual parameter is not always a plain value. The expression Math.sqrt(z+1) and the comparison z >= 10 must each be worked out before anything reaches the subroutine. The program first evaluates z+1, passes that result to the square-root operation, and then passes the single resulting value into the call. The comparison z >= 10 similarly becomes one value before the call proceeds. The subroutine never sees the expression itself, only the value it produced.
Tracing a Divergent Result
The source gives a practical rule for unexpected results: when a result diverges, check the call's actual parameters one by one before looking inside the subroutine. Start at the call, write down the value each actual parameter should have based on your reading of the call, and compare that with what you expected.
Checking actual parameters before reading doTask
Suppose the program's variable z holds 4, and a call to doTask(17, Math.sqrt(z+1), z >= 10) produces a result you did not expect. Where should you look first?
- 1Read the call
There are three actual parameters: 17,
Math.sqrt(z+1), and z >= 10, in that order. - 2Work out the first value
The first actual parameter is 17. Nothing about z affects it, so it should be 17.
- 3Work out the second value
z+1 is 4+1, which is 5.
Math.sqrt(5)is about 2.236. That is the value that should arrive in position 2. - 4Work out the third value
z >= 10 asks whether 4 is at least 10. It is not, so the third actual parameter should be false.
- 5Compare with the result
If the three values arriving in the subroutine match these, the problem lies inside
doTask. If one differs, for example position 2 holds 5 instead of about 2.236, the problem lies in the call.
Checking the three actual parameters one at a time, in position order, identifies whether the divergence starts at the call or inside the subroutine.
- 17 is the first actual parameter, a plain value
Math.sqrt(z+1)is the second actual parameter, an expression evaluated before the call- z >= 10 is the third actual parameter, a comparison that produces a single true or false value
Where ThreeN2.java Uses Subroutines
The source points to a concrete program that uses subroutines: ThreeN2.java. It is introduced in Section 4.4 as an improved version of the 3N+1 program, and the source describes it as a program that uses subroutines and prints its output in neat columns. The source gives two reasons for the use of subroutines. The first is code organization: separating work into named blocks makes the program easier to read and manage. The second is output formatting: the subroutines are part of how the results are printed in neat columns.
The source does not list the individual subroutines inside ThreeN2.java or show their code, so this article does not describe them in detail. When you open the file yourself, look for the places where the program calls a named block of code, and compare them with the places where it prints results. Each call you find is a point where the main program hands work to a subroutine and then continues.
Mistakes When Reading Parameters
Common mistake Treating the call's values as the placeholders inside the definition
What it looks likeLooking at 17 in doTask(17, ...) and assuming it is the name the definition uses internally.
Why it failsValues in the call are actual parameters. Placeholders are formal parameters declared in the definition.
Do this insteadKeep the two apart: the call supplies values, and the definition declares the names that receive them.
Common mistake Reading the parameters in the wrong order
What it looks likeAssuming z >= 10 is the second actual parameter because it is written near the end of the line.
Why it failsThe subroutine receives the actual parameters in the order they appear in the call, so position decides which value goes where.
Do this insteadNumber the actual parameters from left to right before deciding which one matters for a given result.
Common mistake Looking inside the subroutine before checking the call
What it looks likeRereading the body of doTask first when a result seems wrong.
Why it failsThe source says to check the call's actual parameters one by one before looking inside the subroutine.
Do this insteadWrite the expected value of each actual parameter first, then compare those values with what the subroutine should have received.
Common mistake Assuming a subroutine runs when it is defined rather than when it is called
Practice Reading a Call
Consider the call doTask(17, Math.sqrt(z+1), z >= 10). Without looking at the definition of doTask, list the three actual parameters in position order. For each one, say whether it is a plain value, an expression, or a comparison. Then suppose a result seems wrong, and write down which actual parameter you would check first and why.
Hints
- Count the commas inside the parentheses to find the positions.
- The source says each actual parameter is supplied in a fixed position, and the subroutine receives them in that order.
- Check the call before the body of the subroutine.
Key Takeaways
- A subroutine is a block of code that can be called multiple times from different parts of a program, and it runs each time the program calls it.
- A formal parameter is a variable declared in a function definition, while an actual parameter is a value or expression passed when the function is called.
- In
doTask(17, Math.sqrt(z+1), z >= 10), the three actual parameters are a plain value, an expression, and a comparison, and each is supplied in a fixed position. - When a result diverges, check the call's actual parameters one by one, in position order, before looking inside the subroutine.
ThreeN2.javauses subroutines to organize its code and to format its output in neat columns.