# Functions

## Contents

# Functions¶

## A Function *by Any Other Name*¶

Note

Functions, especially very long ones, can make your script quite difficult to navigate. For this reason, it is a good practice to keep as many funcions as possible in a separate file.
Running code from external files can be done by running `include("procedures.jl"),`

where `procedures.jl`

is the name of the file.
You can change your folder by using `cd(...)`

### Functions by `function`

¶

Functions in `Julia`

can be defined in several ways.
One and the most typical is simply by using `function`

.
The structure for using `function`

is as follows:

```
function function_name(argument_1, argument_2)
some_operations
output = some_operations
return output
end
```

An example of defining a very simple functions and its execution:

```
function add_numbers(x, y)
output = x + y
return output
end
```

```
add_numbers (generic function with 1 method)
```

```
@show add_numbers(5, 12)
```

```
add_numbers(5, 12) = 17
```

```
17
```

```
@show w = add_numbers(10,10)
```

```
w = add_numbers(10, 10) = 20
```

```
20
```

Functions do not need arguments:

```
function add_first_ten_numbers()
Σ = sum( 1:10 )
return Σ
end
add_first_ten_numbers()
```

```
55
```

Functions can return many outpus:

```
function add_and_multiply(x, y)
out1 = x+y
out2 = x*y
return out1, out2
end
@show res1, res2 = add_and_multiply(2,3);
```

```
(res1, res2) = add_and_multiply(2, 3) = (5, 6)
```

```
res1
```

```
5
```

```
res2
```

```
6
```

### “Assignment-form” functions¶

There is a compact alternative for `function`

: `f(args) = some operations`

.
Example below:

```
f(x,y,z) = (x^2 + y)/z
@show f(2,1,3)
```

```
f(2, 1, 3) = 1.6666666666666667
```

```
1.6666666666666667
```

Functions in this form can contain multiple lines by using `begin`

/`end`

block:

```
g(x) = begin
y = x^2
y += 1
return(y)
end
```

```
g (generic function with 1 method)
```

### Anonymous functions¶

Sometimes we do not need to create named functions.
Anonymous functions allow to create a normal function without names.
Their structure is `arg -> operations`

with one argument or `(arg1, arg2, arg3) -> operations`

with many arguments:

```
x -> x^2
(x,y) -> x+y
```

```
#3 (generic function with 1 method)
```

We can evaluate functions at some values in the following way:

```
(x -> x^2)(3)
```

```
9
```

`(x -> x^2)`

is the function and `3`

is the argument of this function.

Just like assignment-form functions, anonymous functions can be defined within multiple lines using `begin`

/`end`

blocks:

```
x -> begin
x = x + 1
x = 2*x
return(x)
end
```

```
#7 (generic function with 1 method)
```

`map`

and anonymous functions¶

Recall our example from here. We can use anonymous functions as the argument of `map`

function to compute \(\frac{{x_i}^2-1}{3}\) for each element of vector \(\mathbf{x}:\)

```
x = 1:5;
map(xᵢ -> (xᵢ^2-1)/3, x)
```

```
5-element Vector{Float64}:
0.0
1.0
2.6666666666666665
5.0
8.0
```

Function `map(f, c)`

transform elements of `c`

by applying `f`

(which can be an anonymous but also a named function) to each element.
In most cases this function provides very similar results to `broadcast`

discussed before.
In my own workflow when I work with anonymous functions I use `map`

, while I use `broadcast`

only in the dot (`.`

) convention.

`map`

also works with several lists and multi-argument anonymous functions:

```
map((x,y) -> x^y,
1:4, #array with x elements
[2 2 3 3] #array with y elements
)
```

```
4-element Vector{Int64}:
1
4
27
64
```

## Scopes: minor digression¶

Objects defined outside the function, by default, are not available and their values cannot be changed.

```
σ = 12
function modify_σ()
σ = σ + 1;
return(σ)
end
modify_σ()
```

```
UndefVarError: σ not defined
Stacktrace:
[1] modify_σ()
@ Main ./In[15]:4
[2] top-level scope
@ In[15]:8
[3] eval
@ ./boot.jl:360 [inlined]
[4] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)
@ Base ./loading.jl:1094
```

Sometimes, if we have a *good* reason, we may want to change objects created outside the function and not passed as arguments.
To do it we need to point which objects were created beyond the scope of the function.
This can be done with the use of `global`

:

```
σ = 12
function modify_σ()
global σ
σ = σ + 1;
return(σ)
end
modify_σ()
```

```
13
```

Not only does this function access `σ`

but also changes its value in the global scope:

```
@show σ
```

```
σ = 13
```

```
13
```

It is convenient to use dictionaries as arguments of functions.
Dictionaries passed as the arguments can be unpacked using the macro `@unpack`

, which we already discussed here.
This way, inside the function we can write a clean code without cluttering our global space.
Supposed that we want to have a function that evaluates the function at \(\mathbb{x} \) with parameters *twice as large* as the input arguments \((a, b, c)\).

```
equation = Dict([
("x" , collect(range(1, step=.1, length=100)) ),
("a", 1),
("b", 12),
("c", π)
])
```

```
Dict{String, Any} with 4 entries:
"c" => π
"x" => [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9 … 10.0, 10.1, 10.2…
"b" => 12
"a" => 1
```

```
using Parameters
function multiply_coefficients(X)
@unpack a, b, c = X;
a *= 2;
b *= 2;
c *= 2;
output = deepcopy(X)
#instead of writing:
#output["y"] = @. X["a"]*X["x"]^2 + X["b"]*X["x"] + X["c"]
#we have:
output["y"] = @. a*x^2 + b*x + c
output["a"] = a;
output["b"] = b;
output["c"] = c;
return output
end
```

```
multiply_coefficients (generic function with 1 method)
```

```
new_result = multiply_coefficients(equation)
```

```
Dict{String, Any} with 5 entries:
"c" => 6.28319
"x" => [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9 … 10.0, 10.1, 10.2…
"b" => 24
"a" => 2
"y" => 62.2832
```

The result of executing function is assigned to `new_result`

.
Inside function `multiply_coefficients`

, we were referring to `a`

, `b`

, `c`

normally.
However, after function execution unpacked arguments are still unavailable in the global scope:

```
@show a
```

```
UndefVarError: a not defined
Stacktrace:
[1] top-level scope
@ show.jl:955
[2] eval
@ ./boot.jl:360 [inlined]
[3] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)
@ Base ./loading.jl:1094
```

## Caveat on passing arrays and scalars in functions¶

Consider the following code:

```
using LinearAlgebra
x = 2
y = [1 2;3 4]
set_to_1(scal, matrix)= begin
scal = 1;
matrix .= 1;
return scal, matrix
end
@show x
@show y
@show set_to_1(x, y)
@show x
@show y;
```

```
x = 2
y = [1 2; 3 4]
add_1(x, y) = (1, [1 1; 1 1])
x = 2
y = [1 1; 1 1]
```

Function `set_to_1`

did not modify scalar `x`

but modified array `y`

.
It is because of how the assignment operator works in `Julia`

.
Notice that `matrix .= 1`

has an assignment operator `=`

, which is applied to an array.
This mean that each element *allocated in the memory* pointed by `matrix`

has a new value.
To understand it better recall our earlier discussion on the assignment operator with arrays (and whole dictionaries).
The result of this operation preserves after the execution of the function is finished.

Note

The behavior of the assignment operator inside functions is quite different from other languages. There is some logic behind it but you have to remember it, especially if you are used to other languages as well.

One way to write the function without changing the input array is as follows:

```
using LinearAlgebra
x = 2
y = [1 2;3 4]
set_to_1(scal, matrix)= begin
scal = 1;
matrix_op = deepcopy(matrix) #new line
matrix_op .= 1; #we use `matrix_op` instead of `matrix`
return scal, matrix_op
end
@show x
@show y
@show set_to_1(x, y)
@show x
@show y;
```

```
x = 2
y = [1 2; 3 4]
add_1(x, y) = (1, [1 1; 1 1])
x = 2
y = [1 2; 3 4]
```

## Multi dispatch¶

to be written

```
function Fun1(x::Float64,y::Float64)
return(x-y)
end
```

```
Fun1 (generic function with 2 methods)
```

```
function Fun1(x::Array{Float64},y::Array{Float64})
return(x .- y)
end
```

```
Fun1 (generic function with 3 methods)
```

```
Fun1([1; 2], [3; 2])
```

```
2-element Vector{Int64}:
-2
0
```

```
Fun1(3,2)
```

```
1
```