Modular Abstraction

Intro to Programming Logic COMP-1029

Red River College

Introduction

37786433

This module will focus on the creation of functions, self-contained named blocks of programming code.

Until now we’ve been using Processing’s built-in functions like line, size and ellipse to write our programs, but it’s also possible to create our own functions.

Custom functions allow us to remove redundant code from our programs and make our source code easier to read and update.

Objectives

Upon completion of this module, you should be able to:

Functions are Verbs

Reuse_tcm15-35290

A function is a named, independent section of code that performs a specific task and optionally returns a computed value.

Functions are the verbs of computer programming. They do things.

They increase:

  • Modularity, by allowing us to break down a larger program into smaller parts.
  • Reusability, by allowing us to reuse code without having to retype or copy/paste it.
  • Readability, by allowing us to give descriptive names to specific parts of our program.

Built-In Functions Review

In our Intro to Processing module we learnt how to use Processing’s built in functions. We execute a function by name followed by open and close parenthesis. Many functions allow us to control their behaviour by including a list of comma-separated arguments within the parentheses.

println( "This string is the argument of the println function." );
line( 0, 0, 100, 100 ); // Draw a diagonal line.
fill( 0 ); // Set the fill colour to black.
circle( 50, 50, 40, 40 ); // Draw a black circle.

What's in a Name?

Name

What does this code do?

for( int i = 60; i > 0; i -= 10 ) {
  ellipse( 50, 50, i, i );
}

Writing readable code is important. Wouldn’t it be nice if we could describe this code with an appropriate name?

Paste this code into Processing to see if you were able to correctly determine what it does.

Creating Functions

A generic function looks like this:

returnType functionName( parameters ) {
  // Code goes here.
}

Let’s give our target drawing code a name by defining a function:

void drawTarget() {
  for( int i = 60; i > 0; i -= 10 ) {
    ellipse( 50, 50, i, i );
  }
}

Function names are written in camelCase, just like variable names.

A return type of ”void” means that our function will not return a computed value to the calling program. Later in this module we’ll explore functions that do return values.

Note: The code within a function’s code block will only be executed when the function is called.

Naming Functions

It is important that we give our functions descriptive names. A well thought-out name allows readers of our code to understand the purpose of a function without having to look at the function’s code. This improves the readability of our code.

Our drawTarget function says exactly what it does, it draws a target!

Calling a Function

In our Booleans and Conditionals assignment you were introduced to two special functions called setup and draw.

Whenever we wish to use custom functions in our code, we must use one or both of these special functions.

void drawTarget() {
  for( int i = 60; i > 0; i -= 10 ) {
    ellipse( 50, 50, i, i );
  }
}

void setup() {
  drawTarget(); // Execute or "call" our custom function.
}

Note that we always include the open and close parentheses when we call the function.

If you write a Processing sketch that includes a custom function and you do not use a setup or draw function you will get an error when trying to run your code. Here’s an example of some code that would not run:

void drawTarget() {
  for( int i = 60; i > 0; i -= 10 ) {
    ellipse( 50, 50, i, i );
  }
}

drawTarget();

If you tried to run the above code you’d get an error that say:

It looks like you're mixing "active" and "static" modes."

Function Parameters and Arguments

Currently, our target drawing function does the same thing every time it’s called. What if we could specify where we wanted the target drawn on the canvas?

void drawTarget( int x, int y ) {
  for( int i = 60; i > 0; i -= 10 ) {
    ellipse( x, y, i, i );
  }
}

Here we’ve added two integer parameters to the function definition. These parameters act like variable declarations. When we call the function, we need to provide one argument for each function parameter.

void setup() {
  drawTarget(20, 25);
}

The integer “20” is the first argument and is loaded into the parameter x. The integer “25” is the second argument and is loaded into the parameter y.

We can make our function even more flexible by adding another parameter for the size of the target:

void drawTarget( int x, int y, int size ) {
  for( int i = size; i > 0; i -= 10 ) {
    ellipse( x, y, i, i );
  }
}

Drawing with Targets

Our drawTarget function could be further enhanced to include a parameter for the colour of the target. Here’s a simple drawing program that makes use of this enhanced version of our function.

void setup() {
  size( 400, 400 );
}

void draw() {
  if ( mousePressed ) {
    drawTarget( mouseX, mouseY, 60, #6666EE );
  } else {
      drawTarget( mouseX, mouseY, 60, #66EE66 );
  }
}

void drawTarget( int x, int y, int size, color c ) {
  fill( c );
  for( int i = size; i > 0; i -= 10 ) {
    ellipse( x, y, i, i );
  }
}
Error: Embedded data could not be displayed.

Hover your mouse over the above canvas area. Holding down the mouse button changes the target colour.

Calculating and Returning Values

Functions can return computed values to the program that calls them. Our drawTarget function doesn’t return any data so it was assigned a return type of ”void”.

We use the keyword ”return” to return data from a function. The data we return must match the return type of the function.

The return type must be a valid variable type like int, float, String, etc.

int cubed( int x ) {
  return x * x * x; // return x to the power of 3 as an integer.
}

void setup() {
  int ten_cubed = cubed( 10 );
  println( "Ten to the power of three is " + ten_cubed );
}

Local Variables

You should consider functions to be completely “stand alone” when it comes to variables. Variables created within a function are called local variables. These local variables are not accessible from outside of the function.

In the example below, the variable x_cubed is a local variable, accessible only within the cubed() function. Trying to access it from outside the function will result in an error.

int cubed( int x ) {
  int x_cubed = x * x * x; // The variable x_cubed is accessible only within the function.
  return x_cubed; 
}

void setup() {
  int ten_cubed = cubed( 10 );
  println( "Ten to the power of three is " + ten_cubed );
  println( x_cubed ); // Results in error: "Cannot find anything called 'x_cubed'"
}

Function parameters are also not accessible outside the function. Trying to access the cubed() function’s parameter x from outside the function will result in an error, as shown below.

void setup() {
  int ten_cubed = cubed( 10 );
  println( "Ten to the power of three is " + ten_cubed );
  println( x ); // Results in an error! 
}

Drawing and Moving a Spaceship

Imagine we wanted to code a Space Invaders-type game. We might start out by coding the display and controls for the player spaceship.

Notice that the draw() function only contains three function calls. One to the built in function background and then two custom functions.

int currentShipXPosition;
 
void setup() {
  size(400,300);
  currentShipXPosition = width/2;
}
 
void draw() {
  background(200);
  drawSpaceship(currentShipXPosition);
  currentShipXPosition = calculateNextShipXPosition(currentShipXPosition);
}

Spaceship Functions

The drawSpaceship function is defined with a single parameter and a return type of ”void”.

void drawSpaceship(int topOfShipX) {
  // Drawing code goes here.
}

The topOfShipX parameter controls the x-axis location of where the ship will be drawn on the canvas.

The calculateNextShipXPosition function takes in the current X position of our ship (the currentShipXPosition parameter). It then calculates and returns a new X position based on user input.

int calculateNextShipXPosition( int currentShipXPosition ) {
  // Calculate and return the next x-axis position 
  // based on user input.
}

The actual implementation of these two functions isn’t important. The important thing to realize is that by using functions we’ve assigned descriptive names to potentially complex pieces of code.

Click on the square below and then use the left and right arrow keys to control the “spaceship” (aka the triangle).

Error: Embedded data could not be displayed.

To view the full source code, including the drawSpaceship and calculateNextShipXPosition functions, click on the “source” link above.

Functions vs Procedures vs Subroutines

You will sometimes hear programmers refer to functions as procedures, methods, or even subroutines.

In some programming languages there is a distinction between a procedure (which performs a task) and a function (which calculates a value). This distinction does not exist in Processing.

Object-oriented Programming and Methods

Object-oriented programming (OOP) is a programming style that represents concepts as “objects” that have data fields (attributes that describe the object) and associated functions.

When you start coding Object Oriented Java in the BIT program you will use the term method to refer to functions associated with objects.

In a sense, you can think of the above Spaceship Processing sketch as an object representing a spaceship. The attributes that describe this spaceship object are the integer variables defined at the top of the sketch. The associated methods are the two custom functions used to display and move the ship.

What's Next?

This is the final module in the course. :)

I hope you have enjoyed your introduction to the Java programming language by way of Processing.