Functions

In these notes you will learn:

  • The terminology for describing and using functions in Processing.
  • How to write a variety of simple functions in Processing.
  • What a void function is.
  • The difference between local and global variables.

Introduction

You are no doubt familiar with functions from mathematics. Functions in Processing are, essentially, a more general-purpose version of mathematical functions. They have many similarities, but also some important differences.

In mathematics, a function is a rule that pairs an input value with an output value. For example, in math we can define a function \(f\) like this:

\[f(x) = x^2\]

This function is named f, and it takes a single number as input called \(x\). It’s understood that \(x^2\) are numbers.

We can use \(f\) in calculations. For example, \(f(5) = 5 \times 5 = 25\), and we would say that \(f(5)\) evaluates to 25. We can also use it within expressions, such as \(2 + 3f(2)\), which evaluates to 14.

Now lets write the equivalent of \(f\) in Processing:

float f(float x)
{
   return x * x;
}

Here’s how it can be used:

float a = f(5);
float b = f(-3) + f(a);

println(a);  // 25.0
println(b);  // 634.0

A major difference between a mathematical function and Processing is formality: the rules for defining a function in Processing are much stricter than the rules in mathematics. Processing insists that you explicitly state the type of the input and output values for the function.

Processing functions are more general than mathematical functions. For example, there is no mathematical equivalent of this:

float f(float x) {
   println("debug: x = " + x);
   return x * x;
}

This is an ordinary Processing function, but this doesn’t make sense in mathematics — where would it print?

Parts of a Function Definition

Here again is the definition of the function f from above, but this time we’ve given it the more descriptive name square:

float square(float x)
{
  return x * x;
}

Lets look at the details of this definition:

  • The top line of the function is called it’s header, or signature:

    float square(float x)  // header
    

    The rest of the function’s code under the header is called its body:

    {
      return x * x;  // body
    }
    

    This particular example only has one statement in its body, but in general a function body can have any number of statements.

  • The return type of the function is float, which means that when square(x) is called it always returns a float value. Thus, you can call square anywhere in your program where a float can be used.

  • The name of the function is square. Function names must follow a few rules:

    • Names must be at least one character long.
    • Name characters must be letters, numbers, or the _ character. Spaces, puncuation symbols, and other kinds of characters are not allowed.
    • Names must start with a letter, or the underscore character, _.
    • Names cannot be Processing keywords. For example, you can’t name a function if, or float.
  • The input to the function is a single value of type float. Inside the body of the function, this value is called x.

  • The return keyword causes the function to immediately end. It makes the function evaluate to the value of x * x.

An expression like square(5) is a function call, i.e. we say it calls the square function.

Some Simple Functions

To get familiar with defining and using functions, lets write a few basic functions. Many of these already exist in Processing, but it is good practice to know how to implement them yourself.

Area of a Circle

Here’s a function that calculates the area of a circle:

float circleArea(float radius) {
  return 3.14 * radius * radius;
}

We could have written it like this using the square function:

float circleArea(float radius) {
  return 3.14 * square(radius);
}

Notice that nothing stops you from passing in a nonsensical value, like -4:

println(-4); // 50.24

It’s up to the programmer to know that circleArea only makes sense when radius > 0.

Area of a Rectangle

This function calculates the area of rectangle of width w and height h:

float rectArea(float w, float h) {
  return w * h;
}

Notice how a , is used to separate the parameters in the function header.

If you want to calculate the are of a square with side-length s, you could do this:

float squareArea(float s) {
  return s * s;
}

Or, instead:

float squareArea(float s) {
  return rectArea(s, s);
}

Absolute Value

In mathematics, the absolute value \(|x|\) of a number is defined as follows:

  • If \(x < 0\), then \(|x| = -x\).
  • If \(x \geq 0\), then \(|x| = x\).

In Processing we can write it like this:

float absVal(float x) {
  if (x < 0) {
    return -x;
  } else {
    return x;
  }
}

We call this absVal instead of abs because Processing already implements the abs function.

Here’s how you can use it:

float x = -2;
float y = absVal(3) - absVal(-1);

println(5 - absVal(x)); // 3

Notice that in the body of absVal there are two return statements. However, only one can ever be executed because they are in an if/else structure.

The constrain Function

Processing has a useful built-in function called constrain(x, lo, hi) that works like this:

  • if x < lo, it returns lo
  • if x > hi, it returns hi
  • otherwise, it returns x

In other words, it constrains x to be in the range lo to hi.

Here is how you could implement it (we call it myConstrain to distinguish it from the built-in constrain):

float myConstrain(float x, float lo, float hi) {
  if (x < lo) return lo;
  if (x > hi) return hi;
  return x;
}

Recall that as soon as a return is executed, the function immediately ends. And so, for example, if x is less than lo, the return statement in the first statement will run, and all the code afterwards will not be executed.

Another way to write this function is like this:

float myConstrain(float x, float lo, float hi) {
  if (x < lo) {
    return lo;
  } else if (x > hi) {
    return hi;
  } else {
    return x;
  }
}

It’s more typing, but some programmers prefer this way of writing it because it is more explicit.

Here’s an example of you might use myConstrain in a program:

float myConstrain(float x, float lo, float hi) {
  if (x < lo) return lo;
  if (x > hi) return hi;
  return x;
}

void setup() {
  size(500, 500);
  smooth();
  noStroke();
}

void draw() {
  background(255);

  float x = myConstrain(mouseX, 50, 500 - 50);
  float y = myConstrain(mouseY, 50, 500 - 50);
  fill(255, 0, 0);
  ellipse(x, y, 100, 100);
}

This draws a circle that follows the mouse pointer, but, thanks to the use of myConstrain, no part of the circle ever goes off the edge of the screen.

void Functions

In Processing, functions that don’t return a value are called void functions. For example:

void greeting(String name) {
    println("Good morning " + name + "!");
}

You would call them like this:

greeting("Will");          // "Good morning Will!"

The return type of greeting is void, which means it doesn’t return a value. Instead, it just executes the code in it’s body.

We’ve already been using void functions, e.g.:

void setup() {
  // ...
}

void draw() {
  // ...
}

Both setup and draw are void, which means they don’t return any value.

Another useful function built into Processing is mousePressed:

void setup() {
  // ...
}

void draw() {
  // ...
}

void mousePressed() {
  println("mouse pressed");
}

mousePressed is called automatically whenever you press a button on your mouse.

Functions with No Parameters

In addition to having a void return type, the functions setup, draw, and mousePressed also take no input parameters. Such functions are not common in mathematics, but they are used all the time in programming.

Here’s another example:

void hello() {
  println("Hello!");
}

void goodbye() {
  println("So long!")
}

You can call it like this:

void setup() {
  hello();    // prints "Hello!"
  goodbye();  // prints "So long!"
}

Global and Local Variables

An important detail that arises in the use of functions is the distinction between global variables and local variables:

  • A local variable is a variable declared inside the body of a function, or in the input list of the function’s header. Local variables can only be accessed inside the function they are declared.
  • A global variable is a variable declared outside of any function.

For example:

float x = 0;   // x is global
float y = 0;   // y is global

void setup() {
    // ...

    float a = 2 * x;  // a is local to setup()
    float b = 2 * y;  // b is local to setup()

    // ...
}

Note

Processing is a dialect of the Java programming language. One of the major difference between them is that Java does not allow global variables, while Processing does. Global variables make it a little easier for beginners to learn Processing, but in larger programs they are often the source of subtle bugs, and experience shows that it is usually wise to avoid — or at least minimize — their use.

Questions

  1. Write a small function definition (both the header and the body) named add5 that takes one float as input, and returns, as a float, that input incremented by 5. For example, add5(3) returns 8, and add5(-2) returns 3.

  2. Write the following functions:

    1. circle_circumference(float radius) calculates and returns the circumference of a circle. Recall that the circumference of a circle with radius \(r\) is \(2\pi r\).
    2. square_perimeter(float side) calculates and returns the perimeter of a square with side-length side.
    3. hexagon_area(float side) calculates and returns the area of a regular hexagon with side- length side. If the hexagon has side length \(s\), then it’s area is \(\frac{3\sqrt 3}{2} s^2\). Processing has a square-root function that you might want to use in your answer.
    4. myMin(x, y), which returns the smaller of the ints x and y. For example, myMin(2, 3) returns 2, while myMin(8, -6) returns -6.
    5. myMax(x, y), which returns the larger of the ints x and y. For example, myMax(2, 3) returns 3, while myMax(8, -6) returns 8.
  3. What is a void function?

  4. Consider the following functions:

    void f() {
      println(25);
    }
    
    float g() {
      return 25;
    }
    

    Using these functions as examples, explain the difference between printing a value and returning a value.

  5. Explain the difference between a global variable and a local variable.