Many of the Julia functions we have used, such as the math functions, produce return values. But the functions we’ve written are all void: they have an effect, like printing a value or moving a turtle, but they don’t have a return value. In this lecture you will learn to write fruitful functions.

```
In [2]:
```e = exp(1.0)
radius = 1.0
radians = π/3
height = radius * sin(radians);

`nothing`

:

```
In [6]:
```function print_n(s, n)
for i in 1:n
println(s)
end
end
println(print_n("Hello, World!", 3))

```
```

```
In [1]:
```function area(radius)
a = π * radius^2
return a
end

```
Out[1]:
```

```
In [2]:
```function area(radius)
return π * radius^2
end

```
Out[2]:
```

On the other hand, *temporary variables* like `a`

can make debugging easier.

Sometimes it is useful to have multiple return statements, one in each branch of a conditional:

```
In [3]:
```function absolute_value(x)
if x < 0
return -x
else
return x
end
end

```
Out[3]:
```

Since these return statements are in an alternative conditional, only one runs.

As soon as a return statement runs, the function terminates without executing any subsequent statements. Code that appears after a return statement, or any other place the flow of execution can never reach, is called *dead code*.

```
In [5]:
```function absolute_value(x)
if x < 0
return -x
end
if x > 0
return x
end
end

```
Out[5]:
```

`x`

happens to be 0, neither condition is true, and the function ends without hitting a return statement. If the flow of execution gets to the end of a function, the return value is `nothing`

, which is not the absolute value of 0.

```
In [9]:
```println(absolute_value(0))

```
```

By the way, Julia provides a built-in function called `abs`

that computes absolute values.

*return type* can be specified in Julia and the returned value is converted to this type if possible:

```
In [13]:
```function absolute_value(x) :: Float64
if x < 0
return -x
end
x
end
absolute_value(-3)

```
Out[13]:
```

`return`

keyword is not needed for the final expression in a function body. Its value is automatically returned.

In Python the return type can not be specified and the `return`

keyword is compulsory:

```
def absolute_value(x):
if x < 0:
return -x
return x
```

In C++ the return type has to be specified as the first element of the header and the `return`

keyword is also compulsory:

```
double absolute_value(double x){
if (x < 0.0) {
return -x;
}
return x;
}
```

The type of the arguments needs to be specified. In Julia this is also possible but this will be explained later.

As you write larger functions, you might find yourself spending more time debugging.

To deal with increasingly complex programs, you might want to try a process called incremental development. The goal of incremental development is to avoid long debugging sessions by adding and testing only a small amount of code at a time.

As an example, suppose you want to find the distance between two points, given by the coordinates $(x_1, y_1)$ and $(x_2, y_2)$. By the Pythagorean theorem, the distance is:

$$ d = \sqrt{(x_2 − x_1)^2 + (y_2 − y_1)^2} $$The first step is to consider what a distance function should look like in Julia. In other words, what are the inputs (parameters) and what is the output (return value)?

In this case, the inputs are two points, which you can represent using four numbers. The return value is the distance represented by a floating-point value.

Immediately you can write an outline of the function:

```
In [16]:
```function distance(x₁, y₁, x₂, y₂)
0.0
end

```
Out[16]:
```

Obviously, this version doesn’t compute distances; it always returns zero. But it is syntactically correct, and it runs, which means that you can test it before you make it more complicated.

To test the new function, call it with sample arguments:

```
In [17]:
```distance(1, 2, 4, 6)

```
Out[17]:
```

At this point we have confirmed that the function is syntactically correct, and we can start adding code to the body.

A reasonable next step is to find the differences $x_2 − x_1$ and $y_2 − y_1$. The next version stores those values in temporary variables and prints them:

```
In [18]:
```function distance(x₁, y₁, x₂, y₂)
dx = x₂ - x₁
dy = y₂ - y₁
println("dx is ", dx)
println("dy is ", dy)
0.0
end

```
Out[18]:
```

`"dx is 3"`

and `"dy is 4"`

. If so, we know that the function is getting the right arguments and performing the first computation correctly. If not, there are only a few lines to check.

```
In [19]:
```distance(1, 2, 4, 6)

```
Out[19]:
```

Next we compute the sum of squares of `dx`

and `dy`

:

```
In [20]:
```function distance(x₁, y₁, x₂, y₂)
dx = x₂ - x₁
dy = y₂ - y₁
d² = dx^2 + dy^2
print("d² is ", d²)
0.0
end

```
Out[20]:
```

Again, you would run the program at this stage and check the output (which should be 25).

```
In [21]:
```distance(1, 2, 4, 6)

```
Out[21]:
```

Finally, you can use `sqrt`

to compute and return the result:

```
In [2]:
```function distance(x₁, y₁, x₂, y₂)
dx = x₂ - x₁
dy = y₂ - y₁
d² = dx^2 + dy^2
sqrt(d²)
end

```
Out[2]:
```

If that works correctly, you are done.

```
In [25]:
```distance(1, 2, 4, 6)

```
Out[25]:
```

The final version of the function doesn’t display anything when it runs; it only returns a value. The print statements we wrote are useful for debugging, but once you get the function working, you should remove them. Code like that is called *scaffolding* because it is helpful for building the program but is not part of the final product.

When you start out, you should add only a line or two of code at a time. As you gain more experience, you might find yourself writing and debugging bigger chunks. Either way, incremental development can save you a lot of debugging time.

The key aspects of the process are:

Start with a working program and make small incremental changes. At any point, if there is an error, you should have a good idea where it is.

Use variables to hold intermediate values so you can display and check them.

Once the program is working, you might want to remove some of the scaffolding or consolidate multiple statements into compound expressions, but only if it does not make the program difficult to read.

As you should expect by now, you can call one function from within another. As an example, we’ll write a function that takes two points, the center of the circle and a point on the perimeter, and computes the area of the circle.

Assume that the center point is stored in the variables $x_c$ and $y_c$, and the perimeter point is in $x_p$ and $y_p$. The first step is to find the radius of the circle, which is the distance between the two points. We just wrote a function, distance, that does that:

```
radius = distance(xc, yc, xp, yp)
```

The next step is to find the area of a circle with that radius; we just wrote that, too:

```
result = area(radius)
```

Encapsulating these steps in a function, we get:

```
In [3]:
```function circle_area(xc, yc, xp, yp)
radius = distance(xc, yc, xp, yp)
result = area(radius)
end

```
Out[3]:
```

`radius`

and `result`

are useful for development and debugging, but once the program is working, we can make it more concise by composing the function calls:

```
In [4]:
```function circle_area(xc, yc, xp, yp)
area(distance(xc, yc, xp, yp))
end
circle_area(1, 2, 4, 6)

```
Out[4]:
```

```
In [30]:
```function is_divisible(x, y) :: Bool
if mod(x, y) == 0
return true
else
return false
end
end

```
Out[30]:
```

`is_divisible`

returns either `true`

or `false`

to indicate whether `x`

is divisible by `y`

.
Here is an example:

```
In [31]:
```is_divisible(6, 4)

```
Out[31]:
```

`==`

operator is a boolean, so we can write the function more concisely by returning it directly:

```
In [ ]:
```function is_divisible(x, y)
mod(x, y) == 0
end

Boolean functions are often used in conditional statements:

```
In [32]:
```x = 6
y = 3
if is_divisible(x, y)
println(x, " is divisible by ", y)
end

```
```

It might be tempting to write something like:

```
In [33]:
```if is_divisible(x, y) == true
println(x, " is divisible by ", y)
end

```
```

But the extra comparison is unnecessary.

We have only covered a small subset of Julia (Python, C++), but you might be interested to know that this subset is a complete programming language, which means that anything that can be computed can be expressed in this language. Any program ever written could be rewritten using only the language features you have learned so far (actually, you would need a few commands to control devices like the mouse, disks, etc., but that’s all).

Proving that claim is a nontrivial exercise first accomplished by Alan Turing, one of the first computer scientists (some would argue that he was a mathematician, but a lot of computer scientists started as mathematicians). Accordingly, it is known as the Turing Thesis.

To give you an idea of what you can do with the tools you have learned so far, we’ll evaluate a few recursively defined mathematical functions. A recursive definition is similar to a circular definition, in the sense that the definition contains a reference to the thing being defined. A truly circular definition is not very useful:

*vorpal*:

```
An adjective used to describe something that is vorpal.
```

If you saw that definition in the dictionary, you might be annoyed. On the other hand, if you looked up the definition of the factorial function, denoted with the symbol !, you might get something like this: \begin{align} 0! &= 1\nonumber\\ n! &= n (n−1)!\nonumber \end{align} This definition says that the factorial of $0$ is $1$, and the factorial of any other value, $n$, is $n$ multiplied by the factorial of $n−1$.

So $3!$ is $3$ times $2!$, which is $2$ times $1!$, which is $1$ times $0!$. Putting it all together, $3!$ equals $3$ times $2$ times $1$ times $1$, which is $6$.

`my_factorial`

takes an integer:

```
In [35]:
```function my_factorial(n) end

```
Out[35]:
```

If the argument happens to be 0, all we have to do is return 1:

```
In [36]:
```function my_factoral(n)
if n == 0
return 1
end
end

```
Out[36]:
```

`n−1`

and then multiply it by `n`

:

```
In [82]:
```function my_factorial(n)
if n == 0
return 1
else
recurse = my_factorial(n-1)
result = n * recurse
return result
end
end
my_factorial(3)

```
Out[82]:
```

The flow of execution for this program is similar to the flow of `countdown`

:

Since 3 is not 0, we take the second branch and calculate the factorial of `n-1`

...

- Since 2 is not 0, we take the second branch and calculate the factorial of
`n-1`

...- Since 1 is not 0, we take the second branch and calculate the factorial of
`n-1`

...- Since 0 equals 0, we take the first branch and return 1 without making any more recursive calls.

- The return value, 1, is multiplied by
`n`

, which is 1, and the result is returned.

- Since 1 is not 0, we take the second branch and calculate the factorial of
- The return value, 1, is multiplied by
`n`

, which is 2, and the result is returned. The return value 2 is multiplied by`n`

, which is 3, and the result, 6, becomes the return value of the function call that started the whole process.

`result`

, which is the product of `n`

and `recurse`

.

```
In [71]:
```using TikzPictures
TikzPicture(L"""
\node (main) [draw, fill=lightgray, minimum width=6cm] {$\phantom{3}$};
\node [left of=main, xshift=-3cm] {\textrm{\_\_main\_\_}};
\node (c1) [draw, fill=lightgray, below of=main, yshift=0.25cm, minimum width=6cm] {$\textrm{\tt n}\rightarrow3\ \ \textrm{\tt recurse}\rightarrow2\ \ \textrm{\tt result}\rightarrow6$};
\node [left of=c1, xshift=-3cm] {$\textrm{factorial}$};
\node (c2) [draw, fill=lightgray, below of=c1, yshift=0.25cm, minimum width=6cm] {$\textrm{\tt n}\rightarrow2\ \ \textrm{\tt recurse}\rightarrow1\ \ \textrm{\tt result}\rightarrow2$};
\node [left of=c2, xshift=-3cm] {$\textrm{factorial}$};
\node (c3) [draw, fill=lightgray, below of=c2, yshift=0.25cm, minimum width=6cm] {$\textrm{\tt n}\rightarrow1\ \ \textrm{\tt recurse}\rightarrow1\ \ \textrm{\tt result}\rightarrow1$};
\node [left of=c3, xshift=-3cm] {$\textrm{factorial}$};
\node (c4) [draw, fill=lightgray, below of=c3, yshift=0.25cm, minimum width=6cm] {$\textrm{\tt n}\rightarrow0$};
\node [left of=c4, xshift=-3cm] {$\textrm{factorial}$};
\draw [-latex](c4.north east) to[out=0,in=0,distance=0.4cm] (c3.south east);
\draw [-latex](c3.north east) to[out=0,in=0,distance=0.4cm] (c2.south east);
\draw [-latex](c2.north east) to[out=0,in=0,distance=0.4cm] (c1.south east);
\draw [-latex](c1.north east) to[out=0,in=0,distance=0.4cm] (main.south east);
\node [right of=c4, xshift=2.5cm, yshift=0.4cm] {$1$};
\node [right of=c3, xshift=2.5cm, yshift=0.4cm] {$1$};
\node [right of=c2, xshift=2.5cm, yshift=0.4cm] {$2$};
\node [right of=c1, xshift=2.5cm, yshift=0.4cm] {$6$};
"""; options="very thick, scale=3, transform shape", preamble="""
\\usepackage{newtxmath}
\\renewcommand{\\familydefault}{\\sfdefault}
""")

```
Out[71]:
```

`recurse`

and `result`

do not exist, because the branch that creates them does not run.

Following the flow of execution is one way to read programs, but it can quickly become overwhelming. An alternative is what I call the “leap of faith”. When you come to a function call, instead of following the flow of execution, you assume that the function works correctly and returns the right result.

In fact, you are already practicing this leap of faith when you use built-in functions. When you call `cos`

or `exp`

, you don’t examine the bodies of those functions. You just assume that they work because the people who wrote the built-in functions were good programmers.

The same is true when you call one of your own functions. For example, we wrote a function called `is_divisible`

that determines whether one number is divisible by another. Once we have convinced ourselves that this function is correct —by examining the code and testing— we can use the function without looking at the body again.

The same is true of recursive programs. When you get to the recursive call, instead of following the flow of execution, you should assume that the recursive call works (returns the correct result) and then ask yourself, “Assuming that I can find the factorial of $n−1$, can I compute the factorial of $n$?” It is clear that you can, by multiplying by $n$.

Of course, it’s a bit strange to assume that the function works correctly when you haven’t finished writing it, but that’s why it’s called a leap of faith!

Translated into Julia, it looks like this:

```
In [76]:
```function fibonnaci(n)
if n == 0
return 0
elseif n == 1
return 1
else
return fibonnaci(n-1) + fibonnaci(n-2)
end
end
fibonnaci(7)

```
Out[76]:
```

`n`

, your head explodes. But according to the leap of faith, if you assume that the two recursive calls work correctly, then it is clear that you get the right result by adding them together.

```
In [85]:
```my_factorial(1.5)

```
```

It looks like an infinite recursion. How can that be? The function has a base case—when `n == 0`

. But if `n`

is not an integer, we can miss the base case and recurse forever.

In the first recursive call, the value of `n`

is 0.5. In the next, it is -0.5. From there, it gets smaller (more negative), but it will never be 0.

```
In [10]:
```function my_factorial(n::Int)
if n < 0
println("Factorial is not defined for negative integers.")
return
elseif n == 0
return 1
else
recurse = my_factorial(n-1)
result = n * recurse
return result
end
end

```
Out[10]:
```

*Methods*.

```
In [13]:
```my_factorial(1.5)

```
```

`Int`

blocks nonintegers; the inclusion of a new base excludes negative integers. In the first case a `MethodError`

is generated and in the second, the program prints an error message and returns `nothing`

to indicate that something went wrong.

If we get past both checks, we know that `n`

is positive or zero, so we can prove that the recursion terminates.

This program demonstrates a pattern sometimes called a *guardian*. The first conditional acts as guardians, protecting the code that follows from values that might cause an error. The guardians make it possible to prove the correctness of the code.

Later we will see a more flexible alternative to printing an error message: throwing an *exception*.

Breaking a large program into smaller functions creates natural checkpoints for debugging. If a function is not working, there are three possibilities to consider:

- There is something wrong with the arguments the function is getting; a precondition is violated.
- There is something wrong with the function; a postcondition is violated.
- There is something wrong with the return value or the way it is being used.

To rule out the first possibility, you can add a print statement at the beginning of the function and display the values of the parameters (and maybe their types). Or you can write code that checks the preconditions explicitly by specifying the argument types in the function header.

If the parameters look good, add a print statement before each return statement and display the return value. If possible, check the result by hand. Consider calling the function with values that make it easy to check the result.

If the function seems to be working, look at the function call to make sure the return value is being used correctly (or used at all!).

Adding print statements at the beginning and end of a function can help make the flow of execution more visible. For example, here is a version of factorial with print statements:

```
In [9]:
```function my_factorial(n::Int)
space = " " ^ (4 * n)
println(space, "factorial ", n)
if n == 0
println(space, "returning 1")
return 1
else
recurse = my_factorial(n-1)
result = n * recurse
println(space, "returning ", result)
return result
end
end

```
Out[9]:
```

`space`

is a string of space characters that controls the indentation of the output.

```
In [10]:
```my_factorial(4)

```
Out[10]:
```