7.1 – Functions: Arguments & Return Types

A quick review of what we covered last week. Read last week’s class notes for a more in-depth refresher.

Functions are a way of breaking our code down into smaller, reusable pieces. A function is a sequence of commands that we give a name to, and then we can call that sequence by calling that function’s name.

Defining a function follows these rules:

returnType functionName(){

// code to run

}

We define this function outside of the draw and setup loops. Our functions exist on the same hierarchal level as draw and setup.

We call functions inside of other functions  either setup, draw, mousePressed, or even inside one of our user defined functions.

Here is an example of how we define a function that draws a black ball and how we can call said function in our draw loop.

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

void draw(){
background(255);
displayBall();
} 

void displayBall(){
fill(0);
ellipse(100,100,50,50);
}

The real power of functions come when we can reuse them. I can call displayBall() over and over again and display lots and lots of black balls, but because of the way we currently define our function, we’ll never notice that we’re drawing lots and lots of black balls because they’ll be drawn in the same place (100,100). This is why arguments are important- they allow us to give our functions some specific conditions about how it should operate.

We’ve actually been using arguments all semester. We use them every time we draw a shape! Think of how we draw an ellipse… we need to give it 4 pieces of information in order for it to be displayed the way we want. We have to give it an x and y location, and a width and a height. These are the arguments for an ellipse.

When we look at Processing’s reference page, what we’re really looking at are all the functions Processing already made for us, and the arguments that we need to know when we call those functions. Processing is really just a giant library of functions!

We can give our own functions arguments. Simply declare a local variable in the parenthesis, and then use that variable somewhere in the function.

void drawBlackBall(int diameter){
fill(0);
ellipse(100, 100, diameter, diameter);
}

Now when we call drawBlackBall, we need to give it an int that will be passed into the width and heigh of the ellipse that we’re drawing.

void draw(){
drawBlackBall(50);
}

An important note- now that we defined drawBlackBall to expect an argument, we HAVE to give it an argument when we call it. If we were to call drawBlackBall() without an argument, we would get an error. This is the same reason why we couldn’t call ellipse() without the necessary arguments of x,y,w,h.

A quick note on the words “parameter” vs. “argument”. You will often hear people (myself included) mix up these two words. They stand in for the same idea, however an argument is sent to a function, and a parameter is received. We use arguments when calling our function and parameters when defining our functions. Ex:

drawBlackCircle(argument);
void drawBlackCircle(parameter){

}

We can have more than one parameter in a function. Simply separate the variables by a comma.

void drawBlackBall(int x, int y, int diameter){
fill(0);
ellipse(x,y,diameter,diameter);
}

It’s also ok to mix variable types when you define your parameters.

void drawBlackCircle(int x, int y, int diameter, color c){
fill(c);
ellipse(x, y, diameter, diameter);
}


When you call a function  it is very important that you have the same number of arguments that you define and that they follow the same order and are of the same type as your parameter definition.  In the example above, I’m asking for an int, int, int and color. When I call drawBlackCircle, I have to give the same number of, type and order of arguments– drawBlackCircle(int, int, int, color);

If I try to send a float in the place where an int is expected, I will get an error.

Look at the following diagram from Learning Processing:

Side note- if you have not read Learning Processing Chapter 7, stop what you’re doing right now and read it. It’s short and will clear up a lot of confusion you may be having.

Some other notes about passing arguments-

Parameters act as local variables in their function. Only that function knows about them.

You can send a variable as an argument. This is totally ok-

void draw(){
 int myX = 50;
 int myY = 100;
 int theSize = 75;
 color c = color(255, 0, 0);
 drawBlackCircle(myX, myY, theSize, c);
} 

Now that we know we can call a function multiple times with varying arguments, it seems like an appropriate time to harness the power of the FOR loop! You can use “i” as the argument you pass in.

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

void draw() {
  background(255);
  for(int i = 20; i < width; i+= 20){
  drawBlackCircle(i, 100);
  }
}

void drawBlackCircle(int x, int y) {
  fill(0);
  stroke(255,0,0);
  ellipse(x, y, 20,20 );
}

 

Return types

So far, we haven’t really talked about return types. We’ve come to expect that a function starts out with the word void and then we give our function a name. “void” is actually a return type- it means “return nothing to us”. When we write functions that start out with void, we’re saying, “Here’s a set of instructions for you to draw something to the screen. I don’t want anything back from you.”

But this isn’t always the case. Let’s think of functions that return something to us. random() is a really good example. When we say random(5), we’re asking to have back a random number between 0-5.  It returned a value to us based on a set of conditions (in this case, a random number between 0 and 5).

The return type is the data type that the function returns. In the case of random( ), we did not specify the return type, however, the creators of Processing did, and it is documented on the reference page for random( ). (Hint- it’s a float)

There are more functions like this built into processing. Check out the reference page under “calculation”.

If we want to write our own function that returns a value, we have to specify the type in the function definition. Here’s a simple example:

int sum (int a, int b, int c){
 int total = a + b + c;
 return total;
}

Now, instead of writing void functionName(), we have to write int functionName(), because we are asking this function to return an int to us.

That’s the first time you’re seeing the word “return”. A function with a return type other than void always needs to have a return statement. The value that comes after the word return is what will be handed back to you when you call that function in your program.

As soon as the return statement is executed, the program exits the function and sends the returned value back to the location in the code where the function was called. That value can be used in an assignment operation (to give another variable a value) or in any appropriate expression.  Ex:

int x = sum(5,6,8);
int y = sum(8,9,10) * 2;
int z = sum(x,y,40);
line(100,100,110,sum(x,y,z));

Another figure from Learning Processing. Seriously, seriously, seriously, if you haven’t read Chapter 7 yet DO IT NOW!

 

Another note– Return functions are not variables!! I understand it’s confusing to see:

int functionName(){

}

We see this all the time when we make variables– int x, int rectWidth, etc. etc. It’s very important to note the distinction between the two.

int x = 100; is a VARIABLE. x holds onto the value of 100 until we update it somewhere else in our program.

int x (int xValue){
xValue = xValue + 10;
return xValue;
}

x() is a FUNCTION (and a poorly named one at that, but I’m trying to point something out here). Yes, it returns an int variable to us (xValue), but it is not in and of itself a variable. You also do not need to declare xValue as a global variable. It is a local variable that only needs to exist inside of that function.

If you feel confused about this, think back to when we wrote functions with void. Void doesn’t feel anything like a variable, right? But our return functions are just like our void functions, they just calculate something (an int, float, boolean, color, etc) and give us an answer when we call them.

Return functions don’t always have to return a number. Sometimes you may want to use a function to decide if something is true or false.

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

void draw() {
  background(255);
  boolean test = isXLessThanHundred(mouseX);
  println(test);
}

boolean isXLessThanHundred(int x) {
  if (x < 100) {
    return true;
  }
  else
    return false;
}

Here, we wrote a function asking if a value is less than 100. If it is, that function should return true, if not, return false. When we call this function up in draw, we pass in the value of our mouseX. But, the function is returning a statement to us. We need to do something with that information, otherwise why are we asking for it? In this case, we make a new boolean variable called “test” to hold onto the value returned to the function and print it out to the console, but there’s no reason why we can’t do something like this–

void setup() {
  size(400, 400);
}
void draw() {
 background(255);
 if(isXLessThanHundred(mouseX)){
   rect(width/2, height/2, 100, 100);
  }
}

boolean isXLessThanHundred(int x) {
if (x < 100) {
  return true;
}
else
  return false;
}

In this example, we’re asking the question inside of an IF statement. IF isXLessThanHundred returns true, then execute the line of code that draws a rectangle.

Admittedly, it doesn’t really make sense for us to approach our code like this. Why wouldn’t I just write if(mouseX<100)….. ?  The truth is, that would make more sense for that particular case. Functions that return values are traditionally used to perform complex calculations that may need to be performed multiple times throughout the course of the program.

 An obvious example here is collisions. If you’re programming pong and  trying to test if your ball is hitting the walls or a paddle, or you’re programming brick breaker and need to know if your ball is hitting a rectangle, it’s really convenient to figure out the logic for testing for a collision using variables, and then just pass in the specific variables you need to test when calling up the collision function. You can imagine something like this:

int ballx = 150;
int bally = 100;
int ballw = 50;
int ballh = 50;
int speed = 1;
int rectx = 150;
int recty = 0;
int rectw = 150;
int recth = 50;

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

void draw() {
  background(255);

  if (collision(ballx, bally, ballw, ballh, rectx, recty, rectw, recth)) {
    speed = speed * -1;
    println("collision!");
  }

  ellipseMode(CORNER);
  rect(rectx, recty, rectw, recth);
  ellipse(ballx, bally, ballw, ballh);
  ballx = ballx + speed;
  bally = bally - speed;
  recty = recty + speed;
  rectx = rectx - speed;
}

boolean collision(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) {
  if ((x1 < 0) || (x1 + w1 > width) || (x2 < 0)|| (x2 + w2 > width) || (y1 < 0) || (y1 + h1 > height) || (y2 < 0) || (y2 + h2 > height)){ //checking to see if my shapes hit the walls
  println("wall!");
  return true;
  }

  if (x1 > x2+w2 || x1+w1 < x2 || y1 > y2+h2 || y1+h1 < y2) { // checking to see if my shapes are NOT overlapping
    return false;
  }
  else{
    return true;
  }

}

(Use the horizontal scroll to see the full code)

Right now, it’s not so important that you understand the math that figures out if a collision happened, all that matters is that it looks kinda complicated, right? Now that I spent the time using variables to figure out if two shapes have come in contact with one another or a wall, I can now pass in the variables of the shapes I want to test. And I can do this for as many shapes as I want, without having to re-write that complicated math every time!

Think of it like writing the recipe for testing for a collision, and then calling up that recipe once you have specific information about the shapes you need to test. It answers true or false, and then I can use that information in the rest of my program.

 

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *