dark mode light mode Search Menu
Search

Functions and Procedures

Steve Jurvetson on Flickr

Editor’s Note: I recently took a class, Pen and Paper Coding, which teaches programming with pen and paper instead of computers and a specific language. Erik Linde, the founder and teacher, let me reprint one part of a class so you can experience this remarkable computer-free and language-free way to learn programming. This article — which has been modified from its original — teaches you about functions and procedures with exercises you can do to test your knowledge.

An introduction to procedures

Let’s say you are building software that controls an industrial robot. The robot has an arm with a grip, and by moving its arm and using the grip, it can sort a bin containing red and green balls, by putting each ball in a new bin that matches the ball’s color.

While this is an easy task for a human, it is a quite complex task for a computer. When writing your code, you might therefore benefit from splitting your code up into smaller chunks before you tackle everything. Such chunks are called procedures in programming. A procedure is a section of code that performs a specific task.

Splitting code into procedures

Let’s make an attempt at identifying the robot’s tasks — this will be our first step when we think about procedures. The robot should be able to:

  • Move its arm to the bin area containing the balls
  • Look at a ball, and figure out if it’s red or green
  • Grip a ball
  • Move its arm to the bucket area
  • Release a ball

In this case, it might be appropriate to write procedures for each of these tasks. Not only will this make our task more manageable, it will also allow us to test specific parts of our code and make sure it is working properly, before moving on to the next task.

Our procedures would need to be appropriately named, and they would need to exist for a very specific purpose. For example, for the above tasks, their corresponding procedure names might be:

move_arm_to_ball_area
detect_ball_color
grip_ball
move_arm_to_bin_area
release_ball

Each of these procedures are actually still quite complex, so let’s see if we can break them down further. If we look at move_arm_to_ball_area, we can reason that a sophisticated robot would probably be equipped with both sensors and actuators (read: motors), to move its arm, and would need to be programmed to read its sensor values (to detect where the arm currently is), as well as code to move the motor by an amount appropriate to reach its destination. We could therefore break up move_arm_to_ball_area into smaller procedures, like so:

detect_arm_position

and

move_arm_by

We could then of course break these procedures down further and further, until we have something small enough to actually write code for. When we have procedures that are small enough to write code for, we would write the code, and then test that code under many different scenarios to make sure it actually worked. We would then move on to the next procedure, write it, test it, etc.

The thinking of taking a big problem and breaking it down into small, manageable pieces, is a tried and tested way of programming, and is called top-down programming. It’s also a tactic we will use oftentimes in this article.

Code re-use and the DRY principle

Another benefit of separating your code into smaller procedures is that the code can be re-used in different locations in your code base. For example, the detect_arm_position would likely be used both by the move_arm_to_ball_area and then move_arm_to_bin_area procedures, and perhaps in many other locations as well.

An important principle of programming is the Don’t Repeat Yourself principle (DRY). This means that you should, never, ever, write the same code twice. Why is this? Not only would you be duplicating your efforts, but more importantly — if you have similar code in several locations of your code base, odds are, that at some point, you will want to tweak that code. And, according to Murphy’s law, which states that anything that can go wrong, will go wrong, you can be certain that you will forget to update your code at least in one location of your code base. This is the same reasoning we had when we discussed constants — by defining your constants once only, and then referring to the constants throughout your code — even if you decide to change the value of the constant at some point, you only have to change it once.

It must be stressed once again never write the same code twice.

If you find yourself writing the same code twice, try to figure out a way to break up your code into a procedure, give the procedure an appropriate name, and then call that procedure instead.

What a procedure looks like

Let’s assume we want to accomplish something extremely simple: we want a procedure that concatenates two strings, combining two or more strings into one. It won’t actually be useful for anything practical yet, but we’ll tweak it in a bit to make it more useful.

We would start by trying to come up with an appropriate name for our procedure. In this case it’s easy: let’s pick the name concatenate_two_strings. We will define a procedure using the keyword proc, followed by the procedure name, followed by two empty parentheses. We would mark the end of the procedure by using the keyword end. Here is what it would look like:

# The code below contains the definition of the procedure

proc concatenate_two_strings()
    # The content of the procedure
    # goes here...
end

Any line that starts with a # sign (a hash sign) is called a comment, and will be ignored by the computer. Comments are written for yourself, or for fellow programmers, and can be tremendously useful for the person reading the code.

Anything that goes in between the proc and the word end now belongs to the procedure. The only content of the procedure are currently a few comments — our procedure doesn’t actually do anything useful yet. We’ll work on that, but first note that we have indented the code that goes inside our procedure.

A quick note on indentation and style

Indenting is an important concept in programming. As your code gets more elaborate, indenting it properly will help you visually see what code belongs where, and where each code block starts and ends. You can think of a code block as all the code that goes inside a procedure. Everything that goes inside a procedure should therefore be indented.

You may choose your own indenting style, for example a tab, or, 2 or 4 spaces for each indentation level. It’s generally down to personal taste whether you use tabs or spaces; the only thing required is that your indentation is consistent throughout your code.

In general, you should always strive to indent your code diligently and properly — it will save you time when you troubleshoot your code, if nothing else!

Back to procedures!

Let’s use what we know about indentation, variables and types to write some code for our concatenate_two_strings() procedure, making sure to indent it properly:

proc concatenate_two_strings()
    var string first_name = "John"
    var string last_name = "Doe"
    var string full_name = first_name + " " + last_name
end

Now the procedure actually does something, although it’s not very useful! Even though the variable full_name will exist momentarily, and for a brief moment in time have the value John Doe — the computer will exit out of the procedure as soon as it hits the end keyword, and any information that existed in our procedure will be gone forever, and there won’t be any way to get it back! We will remedy this later by printing some useful results from our code to the screen using the print() function.

Calling a procedure

Whenever we want to use this procedure from somewhere else in our code, we must call that procedure. When we call a procedure, we simply write its name followed by two parentheses, like so:

concatenate_two_strings()

Calling this procedure runs this code through an interpreter, software that reads our code line by line, translates each line into machine code instructions (i.e. into 0’s and 1’s), and then executes those instructions.

When we run the above statement in our code, the interpreter stops what it’s currently doing, finds the procedure definition for the concatenate_two_strings() procedure, enters into that procedure, and executes the code in the procedure, line by line. Once the interpreter hits the end keyword of the procedure, it exits out of the procedure and returns to whatever it was doing.

To calla procedure, you write its name followed by two parentheses.

Functions — like procedures, but return a value

Since the above procedure was fairly useless, we will now introduce another tool into our toolbox to make our procedure slightly more interesting — we will turn it into a function! Functions are basically identical to procedures, and the terminology is often used interchangeably, but functions have the important distinction that they return a value at the end, whereas procedures don’t.

What our function returns will depend on what we want our function to accomplish, but in our specific case, it would be useful if our function returned the value of the full_name variable at the end (in that case we might actually be getting something useful back from it).

Let’s therefore change our concatenate_two_strings() procedure into a function by making the following changes:

func string concatenate_two_strings()
    var string first_name = "John"
    var string last_name = "Doe"
    var string full_name = first_name + " " + last_name
    return full_name
end

Now, concatenate_two_strings() is a function and no longer a procedure.

Here is how a function differs from a procedure:

  • Instead of using proc to define it, a function is defined by the keyword func.
  • When defining the function, we must specify the type of the value it will return. In our case, we want our procedure to return a value of type string.
  • The return statement, indicated by the return keyword, is placed just before the end keyword — the return statement simply returns an appropriate value to the outside world. Anything that comes after the return statement will be ignored.

Calling a function

If we called concatenate_two_strings() from anywhere in our code, it would actually return John Doe. This means that, in our specific case, you could write concatenate_two_strings() or John Doe interchangeably in your code. There would be no difference to the interpreter as concatenate_two_strings() would just return John Doe.

Generally, when we work with functions, we will want to either retain the return value, or do something useful with it. It is therefore common to save the return value in a new variable, like so:

var string full_name = concatenate_two_strings()

full_name will now contain John Doe, and now we can do whatever we want to with the full_name variable — we can print it to the screen, save it to a file, manipulate it, save it to a database, send it as an email — whatever we want to. As we discussed, variables are used to retain information in a computer program so that you can use that information later.

While our function is now slightly more useful — it actually returns a value that we can use for something — the function is still not very interesting as it will always return John Doe no matter what. Let’s change that.

Input parameters and arguments

In our concatenate_two_strings() function, what if, instead of always returning John Doe, we could actually feed the function with two input values: a first name, and a last name. We could then concatenate those names inside the function, and finally return the concatenated name. This would mean that we would actually have our first useful piece of code.

Input parameters (or parameters, for short), is the way we let the function or procedure know that it is supposed to receive one or more input values. The input parameters are what go inside those empty parentheses when we define our function, and the reason those parentheses were empty earlier was because, up until now, we didn’t have any input parameters to feed our functions values with.

Let’s start over with an empty function:

func string concatenate_two_strings()
    # An empty function
    return nil
end

The return nil statement indicates that at this point, our function returns nothing. nil is how we write null values in this article (a null value signifies an undefined, empty value).

Let’s say we would like to feed the above function with two values: a first name, and a last name. We would introduce two input parameters inside of the parentheses, like so:

func string concatenate_two_strings(string first_name, string last_name)
    # An empty function
    return nil
end

As you can see, we specify the type of both first_name and last_name when we define the function. When we call the function, the input parameters first_name and last_name will take on the values of the arguments, and be available for use inside of the function, just like any other variable. Note that first_name and last_name are only available inside (and not outside!) of the concatenate_two_strings() function.

When we call our concatenate_two_strings() function with two arguments (for example, “John” and “Doe”), we would write as follows:

concatenate_two_strings("John", "Doe")

Note that we must not specify the types of the arguments again; rather we should just specify their values, as done above. Their types have already been defined as strings inside of the function definition.

Inside of the concatenate_two_strings() function, the input parameter first_name would now have the value “John”, and the input parameter last_name would have the value “Doe”. Both of them would be available inside of the function, just like any other variable.

A usable function

Let’s finish our function so that it actually does what we want it to do. Since we will assume that the values of first_name and last_name will already have been supplied when the function was called, we do not have to (and we must not) define them again inside the function. Doing so would either conflict with the already defined arguments, or override them.

With that in mind, the finished function would look like this:

func string concatenate_two_strings(string first_name, string last_name)
    var string full_name = first_name + " " + last_name
    return full_name
end

If we wanted to be really fancy and space efficient, we could actually skip the declaration of full_name entirely, and just return the two concatenated strings immediately, like so:

func string concatenate_two_strings(string first_name, string last_name)
    return first_name + " " + last_name
end

Once again, to call this function from somewhere else in the program, we would do as follows:

concatenate_two_strings("John", "Doe")

Or, even better, call the function, but save its return value to a variable:

var string full_name = concatenate_two_strings("John", "Doe")

Exercises

1. Describe, in your own words what a procedure is.

2. How do procedures differ from functions?

3. What happens when the interpreter encounters the code below, exactly as written? What gets printed to the screen?

proc my_proc()
    print("Hello!")
end

4. What will be printed to the screen upon running this code?

proc my_proc(string str)
    print(str)
    my_proc(str)
end
my_proc(Again, and)

5. Find any errors you can find in this code:

proc concat(a, b)
    print(a + b)
end
concat(a)

6. Imagine that you are designing software that will run a car wash. Please use your imagination and try to list at least three tasks that the car wash will need to accomplish, and give those tasks appropriate function / procedure names. Examples: close_carwash_doors(), start_brush_rotation(), etc.

Answers

1. Describe, in your own words what a procedure is.

A procedure is a section of code that performs a specific task.

2. How do procedures differ from functions?

While functions and procedures are similar, only a function returns a value when it ends. A procedure returns nothing when it ends.

3. What happens when the interpreter encounters the code below, exactly as written? What gets printed to the screen?

proc my_proc()
	print("Hello!")
end

The word Hello! is printed to the screen.

4. What will be printed to the screen upon running this code?

proc my_proc(string str)
	print(str)
	my_proc(str)
end
my_proc("Again, and")

The words Again, and is printed to the screen only one time. A procedure cannot call itself.

5. Find any errors you can find in this code:

proc concat(a, b)
	print(a + b)
end
concat(a)

The concat() procedure requires two values be passed, a and b, not a single value.

Learn More

Pen and Paper Coding

https://www.penpapercoding.com

Erik Linde

https://www.linkedin.com/profile/view?id=936246

Variables, Constants, and Data Types

The first article in this series teaching programming basics with pen and paper.
https://kidscodecs.com/variables-constants-data-types/