On this page:
2.5.1 Exercises 3
2.5.1.1 Question 5

2.5 Defining Functions

Mathematics offers a large set of operations that are defined based on the most basic ones. For example, the square of a number is an operation (also designated as a function) that, given a number, multiplies that number by itself. This function has the following mathematical definition:

\[x^2 = x \cdot x\]

Like in mathematics, it is possible to define the square function in a programming language. In Julia, to obtain the square of the number 5, we write the combination 5*5. In general, given a number x, we square it by writing x*x. All that is left to do is associate a name indicating that, given a number x, we obtain its square by evaluating x*x. This is done in Julia as follows:

square(x) = x*x

In the general case, to define a function in Julia we need to use the following syntax:

name(parameter1, ..., parametern) = body

The function’s parameters are called formal parameters, and they are used in the body of the function to refer to the corresponding arguments. When we write square(5) in the evaluator, the number 5 is the argument of the function. During the calculation this argument is associated with the parameter x. The arguments of a function are also called actual parameters.

The definition of the square function declares that, in order to determine the square of a number x, we should multiply that number by itself, i.e., x*x. This definition associates the word square with a procedure, i.e., a description on how to obtain the desired result. Note that this procedure has parameters allowing its use with different arguments. As an example, we will evaluate the following expressions:

> square(5)

25

> square(6)

36

The previously explained rule to evaluate combinations is also valid for combinations that invoke user-defined functions. The evaluation of the expression square(1+2) first evaluates the 1+2 argument. The resulting value, \(3\), will then replace parameter x in the function. This means that the function’s body is evaluated with all occurrences of x replaced by the value 3 and, therefore, the final value will be the value of the combination 3*3, i.e., 9.

Formally, in order to invoke a function, one must construct a combination in which the first element is an expression whose value is the function we want to invoke, and the remaining elements are expressions whose values are the arguments that the function is supposed to use. The result of the combination’s evaluation is the value calculated by the function for those arguments.

The process of evaluating a combination follows three essential steps:

  1. All elements in a combination are evaluated. The value of the first element must be a function;

  2. The formal parameters are associated to the function’s arguments, i.e., the value of the remaining elements of that combination. Each parameter is associated to an argument, according to the order of parameters and arguments. An error occurs when the number of parameters does not match the number of arguments;

  3. The function’s body is evaluated while considering the associations between parameters and arguments.

To better understand this process, it is useful to break it down to its most elementary steps. The following example shows the process of evaluating the expression \(((1+2)^2)^2\) step by step:

square(square(1+2))
\(\downarrow\)
square(square(3))
\(\downarrow\)
square(3*3)
\(\downarrow\)
square(9)
\(\downarrow\)
9*9
\(\downarrow\)
81

Julia does not distinguish between predefined functions and functions created by the user. This means that, just like predefined functions, user-defined functions can be used to create new functions. For example, after defining the square function, we can define the function that calculates the area of a circle with radius \(r\), using the formula \(\pi * r^2\):

circle_area(radius) = pi*square(radius)

Naturally, during the evaluation of the expression that computes the area of a circle, the square function is invoked. This is visible in the following evaluation sequence:

circle_area(2)
\(\downarrow\)
3.14159*square(2)
\(\downarrow\)
3.14159*(2*2)
\(\downarrow\)
3.14159*4
\(\downarrow\)
12.5664

Defining a function causes an association between a procedure and a name. In order to retrieve this association whenever the function is being used, Julia needs to store the association in the computer’s memory. The memory that contains these associations is called the evaluation environment.

Note that this environment exists only while we are using the program. When the program is shut down, that environment is lost. In order to avoid losing those definitions, they should be saved in a file. This means that, despite the usefulness of the REPL for testing the definitions, these should be written in files to preserve them for future use. Fortunately, programming environments, such as Atom, facilitate this workflow.

2.5.1 Exercises 3
2.5.1.1 Question 5

Define the function double, which calculates the double of a given number.