What are Arguments?
At this point, you should have a good understanding of defining and calling simple functions and an appreciation for the benefits that functions provide. However, functions can be even more useful (and general-purpose) if we can pass them arguments.
An argument is data that we pass into a function when we call it. You can think of arguments as the input(s) to a function. All of the built-in functions we’ve used so far have required arguments. For example, the ellipse() function requires 4 integers (or variables with integer values) to be passed as arguments:
1 2 |
let diameter = 50 ellipse(200, 200, diameter, diameter) |
Function Arguments in Action!
Let’s say we have a simple function called drawNoEntrySign(), as shown in this program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function setup() { createCanvas(400, 400) } function draw() { background(220) drawNoEntrySign() } function drawNoEntrySign() { fill(255,0,0) noStroke() ellipse(width/2,height/2,100,100) fill(255) rect(width/2-40,height/2-10,80,20) } |
The drawNoEntrySign() function is pretty cool, but it always draws the sign at the same position on the canvas every time. It would be more generally useful if we could tell the function where to center the sign when we call it. We’ll do this by adding 2 arguments to the drawNoEntrySign() function to provide an (x,y) coordinate to position the center of the sign.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function setup() { createCanvas(400, 400) } function draw() { background(220) drawNoEntrySign(100, 100) } function drawNoEntrySign(x, y) { fill(255,0,0) noStroke() ellipse(x, y, 100,100) fill(255) rect(x-40, y-10, 80, 20) } |
Look at the drawNoEntrySign() code on lines 10 to 16. Here I’ve added 2 variables x and y inside the ( parentheses ) after the function name. These are called parameters. Parameters receive a copy of the arguments passed into the function (more on this later), and they act as local variables to the function. Since they act as local variables it means that they can only be used within their function. The drawNoEntrySign() function uses the x and y parameters to customize where the ellipse and rectangle shapes will be drawn.
You might think that arguments and parameters are the same things, and there is some truth in that. The data passed into the function when it is called is an argument, and that same data, when received by the function, is called a parameter – what name you use depends on which side of the function call you are on. Outside the function, it’s an argument, inside the function it’s a parameter. An analogy I often use is if I give you a “gift” and you thank me for the “present” we are both talking about the same box, just using different terminology.
In the draw() function, on line 7, I call the drawNoEntrySign() function and pass in 2 arguments. An argument can be a literal value as shown above, or I could have used a variable or math expression instead like this:
1 2 |
let center = 100 drawNoEntrySign(center, center*2) |
There are a couple of additional things to keep in mind. The first is that if a function expects a certain number of parameters, then you must pass that many arguments to it. For example, it would be a syntax error to try and pass 1 or 3 arguments to the drawNoEntrySign() function.
Second, if a function expects a certain kind of data to be passed in be sure to do so. For example, it would be a syntax error to try and pass a “string” as a parameter to drawNoEntrySign().
Finally, before we move on, let’s revisit one of the big benefits of functions: code reuse. Now that I have a function that can draw a “No Entry” sign anywhere on the canvas I like, I can call the function many times and simply change the arguments to draw signs at different positions:
1 2 3 4 5 6 7 |
function draw() { background(220) drawNoEntrySign(100, 100) drawNoEntrySign(300, 100) drawNoEntrySign(100, 300) drawNoEntrySign(300, 300) } |
Or even use a loop to draw many of them at random positions!
1 2 3 4 5 6 7 8 9 10 11 |
function draw() { background(220) // Draw 10 random signs for (let count = 0; count < 10; count++) { drawNoEntrySign(random(50,350), random(50,350)) } // The noLoop() function tells p5.js NOT to call the draw() again noLoop() } |
noLoop() and loop()
You’ll notice in the last example, the use of a new function called noLoop() on line 10. By default, p5.js calls the draw() function automatically ~60 times/second. The noLoop() function tells p5.js that the draw() function should be called only once, and not automatically over and over again.
Try commenting line 10 out and see what happens!
You might also decide to move this into the setup() function instead, it will have the same effect. There is a complementary function called loop() that tells p5.js to repeatedly call draw() again.
Pass-By-Copy (or Pass-by-Value):
There’s one last important detail that is essential for your understanding of how arguments are actually passed to functions. Consider the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function setup() { createCanvas(400, 400); let num = 10 print("1. In setup(), num is " + num) changeNum(num) print("4. In setup(), num is still " + num) } function changeNum(num) { print("2. In changeNum(), num is " + num) num = 5 print("3. In changeNum(), num is now " + num) } |
There is no draw() function here, I’m just trying to demonstrate what happens when an argument is passed to a function. On line 5, a copy of the value stored in the num variable is passed to the changeNum() function. This is referred to as pass-by-copy (or pass-by-value) in Computer Science.
If you run this code, you may be surprised to find that at line 6 the value of num is still displayed in the console as 10 (not 5). Remember that the num variable declared in the setup() function is local to that function, and the num parameter variable defined in the changeNum() header is local to its function. These are two different, independent variables with two different scopes. The argument and parameter names can be the same (or different), it makes no difference to how pass-by-copy works.
This means that if the num parameter variable in changeNum() is modified, it does not affect the value of the num variable defined in setup(). A good analogy is if I email you a copy of a document to work on and you edit that copy it does not change my original document. Similarly, changing a parameter variable does not affect variables outside its function.
You Try!
- Imagine if p5.js had no built-in rect() function. Create a function called rectangle() that requires 4 arguments, x1 and y1 for the top-left corner of the rectangle, and x2, y2 for the bottom-right corner of the rectangle. Inside your rectangle() function use only the built-in line() function to draw the rectangle.
- Customize the final drawNoEntrySign() example so that it also requires 3 RGB colour arguments for the ellipse, and diameter to change the size of the sign. The white rectangle in the middle should adjust itself so that it looks proportional to the circle.
- Modify your drawHappyFace() function from 2-3 Q1 so that it requires 2 arguments: centerX and centerY that are used to position your happy face anywhere within the canvas.
- Add a move() and bounce() function to Q3 to move your happy face around the canvas, bouncing off the edges.
- Try to predict the output of this code, then try it in p5.js to see if you are right!
12345678910111213141516171819202122232425function setup() {noLoop()print( "setup(): Hello functionA!" )functionA()print( "setup(): I'm done!" )}function draw() {print("draw(): Hello functionB!")functionB()print("draw(): I'm back!")functionA()print("draw(): I'm done!")}function functionA() {print("functionA: Yo!")}function functionB() {print("functionB(): Howdy!")// It's possible to call functions within other functions!functionA()print("functionB(): Adios!")}