Concepts / Program Design with Subroutines

Program Design with Subroutines

A subroutine parameter lets the same subroutine perform its task on different inputs, with the caller choosing the input on each call.

  • Programming

Why Repeated Output Needs a Better Home

Suppose you have written a subroutine that prints a line of output. Every time the program calls it, the same line appears. That is useful, but limited. Most useful subroutines need to know something about the situation they are working in: which number to display, which list to process, which label to attach to a result. This article follows a small set of subroutines through a trace of a program run, and shows how a single piece of code can serve many moments in that run.

Writing a Block Once and Calling It Many Times

Imagine a program that needs the same job done in three different places. You could copy the same lines of code three times, or you could write those lines once and let the rest of the program use them whenever they are needed. A subroutine is the second option. It is a block of code that can be called multiple times from different parts of a program.

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. Because the same block can be called more than once, a single piece of code can serve many moments in a single run of the program.

A subroutine's task is also always a subtask of some larger program. A program that does a complicated job is usually divided into smaller pieces, and each piece is handled by a subroutine that does one clearly described thing. Deciding how to split a large job into pieces is a separate design skill from writing any single piece. This article concentrates on writing the pieces.

Tracing Control Through Repeated Calls

Read the following program slowly. It has one subroutine, printLine, and a main part that calls it three times with a different line of ordinary output between two of the calls. The subroutine does not appear in the output by name. Only its body runs, and it runs each time control reaches a call to it.

java
Output
-----
between
-----
-----
Notes on the code
  • main is where the program starts running
  • Each printLine(); line is a call that transfers control to the subroutine
  • printLine has no parameter, so every call performs exactly the same task

Control moves in a predictable pattern. Main runs until it reaches the first call. Control jumps into printLine, which prints one dashed line, and then control returns to the statement in main that came after the call. Main prints the word between, then reaches the second call, and the cycle repeats. The diagram below traces the same three calls in order.

mainprintLine1: call printLine2: return to main3: call printLine4: return to main5: call printLine6: return to main
In what order does control move between main and the subroutine across several calls, and where does control return to after each call?

What do you think happens?

Before reading the output, how many lines of dashes will the Greetings program print?

  • One

  • Two

  • Three

  • Four

Reveal answer
Answer:

Three

main calls printLine three times, and each call runs the same body once, printing one line of dashes. The word between is printed by main itself, not by the subroutine, so it does not count toward the dashed lines.

How a Parameter Supplies the Situation

A subroutine parameter is the mechanism that supplies information the subroutine needs. The caller hands a value to the subroutine, and the subroutine uses that value while it does its job. This is what lets one subroutine behave differently on different calls. The subroutine's instructions stay the same; the input changes.

Definition

A subroutine parameter lets the same subroutine perform its task on different inputs, with the caller choosing the input on each call.

java
Output
3 squared is 9
7 squared is 49
Notes on the code
  • The parameter n is declared in the header inside the parentheses
  • The caller passes 3 on the first call and 7 on the second
  • The body is identical both times; only the value n holds differs
Before
printSquare(3);

The parameter n holds 3, so the body prints 3 squared is 9

After
printSquare(7);

The parameter n holds 7, so the same body prints 7 squared is 49

printSquare(3)prints 3 squared is 9printSquare(7)prints 7 squared is 49
How does the same subroutine produce different results when the caller passes different argument values on each call?

Reading a Subroutine Header

The header of a subroutine names it and declares its parameters. In the Squares example, the header is public static void printSquare(int n). The word void says the subroutine returns no value. The name printSquare is what the caller writes. The declaration int n inside the parentheses says the subroutine expects one integer and will refer to it as n inside the body.

Part of the headerExampleWhat it tells the reader
Return typevoidThe subroutine gives no value back to the caller
NameprintSquareThe name the caller uses in each call
Parameter typeintThe kind of value the caller must supply
Parameter namenThe name the body uses for the supplied value

Parts of a subroutine header and how a call matches them

The call must match the header. The call printSquare(3) supplies one integer, which fits the parameter type int. The name in the call matches the name in the header. A caller who writes a call that supplies a value of the wrong kind has not matched the header, and that mismatch is a common source of errors discussed below.

Passing a Whole Array as One Parameter

A parameter does not have to hold a single number or a single word. Its type can be an array type. When that happens, the caller does not pass the array's elements one at a time. The entire array of values travels into the subroutine as a single parameter, and the subroutine has access to all of its values through that one parameter.

Consider a concrete need. A subroutine should display every value in an integer array in a neat format: the values separated by commas and wrapped in square brackets. The subroutine has to know which array to display. The way to tell it is a parameter of type int[ ], which is the notation for an array of integers. The caller supplies the array when it makes the call, and the subroutine works on that array.

data[0]4data[1]8data[2]15
When the array is passed as one parameter, how do index positions in the caller's array map to the values the subroutine reads?
java
Output
[4, 8, 15]
Notes on the code
  • int[] values in the header declares an array-of-integers parameter
  • The call printArray(data) passes the whole array as one item
  • The loop checks each index; the separator is printed only before values after the first
  • The brackets are printed once, before and after the whole list

Follow the separator logic closely, because it is the part learners most often get wrong. The first value gets no comma in front of it. Every later value gets a comma before it. The brackets surround the whole list, not each value. The check if (i > 0) is what keeps the first value free of a leading comma, since index 0 is the first position in the array.

Subroutines in ThreeN2.java

The source points to a concrete program that uses subroutines: ThreeN2.java. It is introduced 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 relying 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 neat columns are achieved.

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 should correspond to a subtask with one clearly described job.

Mistakes Beginners Make with Subroutines

  • Common mistake

    Copying the same lines in several places instead of calling one subroutine

    What it looks like

    Pasting the dashed-line println statement three times into main

    Why it fails

    Each copy must be edited separately if the output format changes, which is the problem subroutines solve

    Do this instead

    Write the lines once in a named subroutine and call it at each point it is needed

  • Common mistake

    Expecting a subroutine to run just because it is written in the program

    What it looks like

    Defining printLine but never calling it from main, then expecting dashes to appear

    Why it fails

    A subroutine runs only when the program reaches a call to it

    Do this instead

    Place a call to the subroutine at each point in the program where its task is needed

  • Common mistake

    Passing a value that does not match the parameter type

    What it looks like

    Calling printArray with a single number when the header declares int[ ] values

    Why it fails

    The call must supply the kind of value the header declares

    Do this instead

    Pass an array of integers, such as the data array, to match the int[ ] parameter

  • Common mistake

    Putting the separator before the first value when printing a list

    What it looks like

    Printing a comma before every value, which gives a leading comma such as [, 4, 8, 15]

    Why it fails

    The source's formatting rule says there is no comma before the first value

    Do this instead

    Only print the separator when the index is greater than 0

  • Common mistake

    Assuming a subroutine's task is the whole program rather than one subtask of it

Practicing a Parameterized Subroutine

MEDIUM

Write a subroutine printTotal that takes one parameter of type int[ ] and prints the sum of its values on a single line in the form Total: 27. Then call it from main on two different arrays, one of which is {5, 10, 12}. Predict the output of each call before you run it.

Hints
  • Your header should look like public static void printTotal(int[] values), matching the int[ ] parameter type
  • Use a loop from index 0 up to values.length, adding each value to a running total variable
  • Declare the total before the loop and print it after the loop finishes

Key Takeaways

  • A subroutine is a block of code that can be called multiple times from different parts of a program, and it runs only when the program reaches a call to it.
  • A parameter supplies the information a subroutine needs, so the same subroutine can perform its task on different inputs chosen by the caller on each call.
  • A parameter can have an array type such as int[ ], so an entire array travels into the subroutine as one single parameter.
  • Each subroutine is a subtask of a larger program, and ThreeN2.java uses subroutines to organize its code and format its output in neat columns.