Subroutines and Modularity
A subroutine is a named group of instructions for a specific task.
Writing the Same Job Once
Imagine a program that needs the same job done in three different places. One option is to copy the same lines of code three times. The other option is to write those lines once and let the rest of the program use them whenever they are needed. The second option is the idea behind a subroutine. A subroutine is 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, where would you rather keep the instructions for it, in one place or in every place it is needed? The main idea of subroutines is an answer to that question.
What do you think happens?
If a subroutine is written into a program but the program never reaches a point where it is called, does the subroutine run?
Reveal answer
No, it runs only when the program calls it
The important word 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.
Following Calls Through a Run
A subroutine is a named group of instructions for a specific task. Grouping the instructions and giving them a name is what lets the rest of the program refer to the whole group with a single word. The name serves as a compact substitute for the instructions it represents. When the program reaches that name at a point where it calls the subroutine, the instructions stored under the name are carried out.
Because the same block can be called more than once, one piece of code can serve many moments in a single run of the program. The diagram below traces the control flow for a main program that calls the same subroutine three times. Each arrow pointing to the subroutine is a call, and each arrow pointing back is the return. After each return, the main program continues from the point where it left off.
Dividing a Large Program
A large program may need to handle many different tasks. Trying to design all of those tasks as one undivided block makes the program difficult to manage. A subroutine offers a way to separate one task from the rest: its instructions are grouped together, and the group receives a name. This separation is the basis of modularity, the idea that a large program can be divided into manageable units.
Subroutines also change how a programmer approaches a problem. Suppose a program draws a house as one of its tasks. While creating a house-drawing subroutine, attention can stay on drawing the house rather than on every other part of the program. Once that task is organized into a subroutine, the larger design can treat house drawing as an already-solved part of the program. This is what the source means by helping programmers focus on one design problem at a time.
Reuse Instead of Copying
Writing a subroutine once lets the program reuse it at many points, so repeated work does not have to be written out again each time it is needed. The following pseudocode is an illustrative example, not source code. It shows the same drawing job written in three places, first by copying the instructions, then by defining one subroutine and calling it.
- The copied version repeats three lines three times, so any change must be made in three places.
- The subroutine version stores the three lines once under the name
draw_house. - Each call
draw_houseline runs the stored instructions at that point in the run.
draw roof, draw walls, draw door (copy 1) draw roof, draw walls, draw door (copy 2) draw roof, draw walls, draw door (copy 3)
Three separate copies of the same instructions
define subroutine draw_house: draw roof, draw walls, draw door call draw_house call draw_house call draw_house
One stored set of instructions, called three times
Stepping Through Repeated Calls
Tracing a Main Program That Calls One Subroutine Three Times
A main program reaches three points where it needs the same job done. The job is stored in one subroutine. Trace what happens at each point.
- 1First call
The main program reaches the first call. Control jumps to the subroutine, and its instructions run.
- 2First return
When the subroutine finishes, control returns to the main program at the point just after the first call, and the main program continues.
- 3Second call
Later in the same run, the main program reaches the second call. Control jumps to the same stored instructions again.
- 4Second return
The subroutine finishes and control resumes the main program after the second call.
- 5Third call and return
The third call repeats the pattern. The single block of code served three moments in one run.
The subroutine's instructions ran three times, but they were written only once. Each time, execution resumed in the main program right after the call that started it.
A main program calls a subroutine named print_header at four places. How many times do the instructions inside print_header run during one run of the program, and how many times are those instructions written in the source file?
Hints
- A subroutine runs each time the program reaches a call to it.
- Writing a subroutine once means its instructions appear in the file once, no matter how many calls there are.
Subroutines in ThreeN2.java
The source points to ThreeN2.java as a concrete program that uses subroutines. 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 the program relies on 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.
Common Mistakes With Subroutines
Common mistake Expecting a subroutine to run just because it is written in the program
Why it failsA subroutine runs only when the program reaches a point where it calls it.
Do this insteadCheck that a call to the subroutine appears at each place where the task is needed.
Common mistake Copying the same instructions into several places instead of calling one subroutine
Why it failsCopies must all be updated separately whenever the task changes, which makes errors easier to introduce.
Do this insteadWrite the instructions once under a name and call that name wherever the task is needed.
Common mistake Treating the subroutine name as a separate job from its instructions
Why it failsThe name is a compact substitute for the instructions it represents, not a different task.
Checking Your Understanding
A program needs the same printed output in five different places. Describe two ways to organize the instructions for that output, and explain which one matches the source's idea of a subroutine and why.
Hints
- Think about whether the instructions are copied into each place or stored once and called.
- The source says a subroutine is a block of code that can be called multiple times from different parts of a program.
Key Takeaways
- A subroutine is a named group of instructions for a specific task, and its name stands in for those instructions.
- A subroutine runs only when the program calls it, and the same subroutine can be called many times in one run, with execution resuming after each call.
- Subroutines divide a large program into manageable units and let a programmer focus on one design problem at a time.
- Writing repeated work once as a subroutine reduces copied instructions, and
ThreeN2.javauses subroutines to organize code and format its output.