Skip to content
ExamSpine

Lesson 4.8

Capstone: split a long program into functions

Part 4: FunctionsReading, about 6 min1 exercise in this lesson

Java answers can't run on Spine yet. You can still read Java, trace it, and put an answer's lines in order.

Splitting a long program into functions

By the end of this you will be able to plan a longer program as a few small functions plus a main part, decide which job goes where, and test each function before the whole program runs.

This lesson puts Part 4 together. You can write functions that take values and return an answer, call them from a loop, and keep each function's variables to itself. Here you use all of that to build one program from parts.

The task

A cafe sells cups of tea for 3 dollars each, and every fifth cup in an order is free. At the end of the day, the program reads how many orders there were (1 to 50), then the number of cups in each order (1 to 20), one per line. It prints the price of each order, then the day's takings, then how many big orders there were (10 cups or more).

You could write all of this in one block. The price rule would then sit in the middle of the loop, mixed in with the reading, the adding up and the printing. It would work, but to check the price rule you would have to run the whole program and read its output.

Plan: one job per part

Before you write any code, list the jobs the program does. Then decide where each one goes.

JobWhere it goes
Work out the price of one order from its cupsA function that takes the cups and returns the price
Decide whether one order is bigA function that takes the cups and returns true or false
Read the number of orders, and the cups in eachThe main code
Add up the takings and count the big ordersThe main code, in variables made before the loop
Print each order's line and the summaryThe main code

Two rules decide the table:

  • A job that turns a value into an answer becomes a function. It gets the value as a parameter and gives back the answer with a return. It does not read input or print, so you can call it with any value you like and see what comes back.
  • A job that has to remember something over the whole loop stays in the main code. A function's local variables start again on every call, so a running total kept inside a function would never get past its first step.

The program

def order_price(cups):
    free = cups // 5
    return (cups - free) * 3

def is_big(cups):
    return cups >= 10

orders = int(input())
takings = 0
big = 0
for i in range(1, orders + 1):
    cups = int(input())
    cost = order_price(cups)
    takings = takings + cost
    if is_big(cups):
        big = big + 1
    print("Order " + str(i) + ": "
        + str(cost))
print("Takings: " + str(takings))
print("Big orders: " + str(big))
import java.util.Scanner;

public static void main(String[] args) {
    Scanner scanner
        = new Scanner(System.in);
    int orders = Integer.parseInt(
        scanner.nextLine());
    int takings = 0;
    int big = 0;
    for (int i = 1; i <= orders; i++) {
        int cups = Integer.parseInt(
            scanner.nextLine());
        int cost = orderPrice(cups);
        takings = takings + cost;
        if (isBig(cups)) {
            big = big + 1;
        }
        System.out.println(
            "Order " + i + ": " + cost);
    }
    System.out.println(
        "Takings: " + takings);
    System.out.println(
        "Big orders: " + big);
}

static int orderPrice(int cups) {
    int free = cups / 5;
    return (cups - free) * 3;
}

static boolean isBig(int cups) {
    return cups >= 10;
}

If the user types 3, then 7, 4 and 10, the program prints:

Order 1: 18
Order 2: 12
Order 3: 24
Takings: 54
Big orders: 1

Read the loop body on its own and it says what happens to each order: read the cups, find the price, add it to the takings, count the order if it is big, print its line. The details of the price rule are in one place, under a name that says what it does.

The price is worked out once per order and kept in cost, because the loop needs it twice: once for the takings and once for the line it prints.

Test each function on its own

Each function can be checked before the loop uses it. Call it with values you can work out by hand, and choose values at the edges of each rule:

  • 4 cups: no cup is free, so the price is 12.
  • 5 cups: the fifth cup is free, so the price is still 12.
  • 10 cups: two cups are free, so the price is 24.
  • 9 cups is not big, and 10 cups is.

If all of these come back right, any mistake left in the program is in the main code, so you know where to look.

Two common slips

  • A helper that prints its answer instead of returning it. The answer shows on the screen, but the main code gets nothing it can add to the takings. In Python, the call gives back None, and adding that to a number stops the program with an error. In Java, a void method gives back no value, so the line that stores the cost does not compile.
  • A helper that reads the input itself. Then you cannot call it with a value of your choice to test it, and the order in which input lines are read is split across several places. Keep the reading in the main code, and pass each helper the value it needs.

Now try it yourself on the questions below.

Exercise 4.8.14.8.1 · Write itScore report

Write a program that reads several test scores and prints a report with a grade and a star bar for each score, then a summary.

  1. Read the number of scores (a whole number from 1 to 50) on the first line.
  2. Read each score (a whole number from 0 to 100) on its own line.
  3. For each score, print the score, a space, the grade, a space, and a bar of * characters. The bar has one star for every 10 marks (whole-number division by 10).
  4. Grades: 80 or above is Distinction, 50 to 79 is Pass, below 50 is Fail.
  5. After all scores, print Average: followed by the whole-number average (drop the fraction).
  6. Then print Best: followed by the highest score, a space, and its grade.

Write a function for the grade and a function for the star bar. The number of scores is always from 1 to 50. Your program is run several times. Only what it prints is checked.

In Python, use // for whole-number division. In Java, / between two ints already drops the fraction.

Running Java here comes later. You can put this answer's lines in order instead. You can still write your Java answer (it stays while this page is open) and compare it with our worked solution. Or try this exercise in Python.

Examples

Your program is run 7 times: the 3 examples below, and 4 hidden ones that check the rules above. Only what it prints is checked.

  • three scores covering all three grades (rules 3, 4, 5, 6)

    You type

    1. typed: 3
    2. typed: 85
    3. typed: 45
    4. typed: 60

    It prints

    85 Distinction ********
    45 Fail ****
    60 Pass ******
    Average: 63
    Best: 85 Distinction
  • one perfect score (rules 3 and 6)

    You type

    1. typed: 1
    2. typed: 100

    It prints

    100 Distinction **********
    Average: 100
    Best: 100 Distinction
  • boundary scores for Pass and Distinction (rule 4)

    You type

    1. typed: 2
    2. typed: 50
    3. typed: 80

    It prints

    50 Pass *****
    80 Distinction ********
    Average: 65
    Best: 80 Distinction
solution.pySolution.java