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:
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 whensquare(x)
is called it always returns afloat
value. Thus, you can callsquare
anywhere in your program where afloat
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
, orfloat
.
The input to the function is a single value of type
float
. Inside the body of the function, this value is calledx
.The
return
keyword causes the function to immediately end. It makes the function evaluate to the value ofx * 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 returnslo
- if
x > hi
, it returnshi
- 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¶
Write a small function definition (both the header and the body) named
add5
that takes onefloat
as input, and returns, as afloat
, that input incremented by 5. For example,add5(3)
returns 8, andadd5(-2)
returns 3.Write the following functions:
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\).square_perimeter(float side)
calculates and returns the perimeter of a square with side-lengthside
.hexagon_area(float side)
calculates and returns the area of a regular hexagon with side- lengthside
. 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.myMin(x, y)
, which returns the smaller of theint
sx
andy
. For example,myMin(2, 3)
returns 2, whilemyMin(8, -6)
returns -6.myMax(x, y)
, which returns the larger of theint
sx
andy
. For example,myMax(2, 3)
returns 3, whilemyMax(8, -6)
returns 8.
What is a
void
function?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.
Explain the difference between a global variable and a local variable.