In Julia, functions are objects like any other. This offers a number of interesting mechanisms, such as for example the ability for a function to return another function. Let’s use a simple example: the logistic map. It is defined by $x_{n+1} = a\times x_n\times(1-x_n)$, and $x_0 \in [0;1]$.
If we wanted to do this calculation, we could write a function that takes two
arguments, a
and x
, and return a*x*(1.0-x)
. But there is no reason to
expect that $a$ (a parameter) will change, so what we might want to do instead
is return a function with a
“built-in”.
function f(x::Number)
return a * x * (1.0 - x)
end
f (generic function with 1 method)
Of course this function will not work, because a
is currently not defined.
And we do not want to hard-code the value of a
, since we might want to change
this parameter. So ideally, we would like to have a function to write the
function f
on our behalf.
Because functions are objects like any other, they can actually be returned. So
we can write a function that accepts a
as its argument, and then returns a
function that accepts x
as an argument:
function logistic(a::Number)
return function f(x::Number)
return a * x * (1.0 - x)
end
end
logistic (generic function with 1 method)
Now, if we call this function, we can see that it does indeed return a function:
logistic(1.2)
(::Main.##WeaveSandBox#253.var"#f#1"{Float64}) (generic function with 1 met
hod)
We can now use this function to run our model:
m = logistic(1.4)
x0 = 0.5
m(x0)
0.35
This specific design pattern shows up in a bunch of Julia functions:
isequal
, for example, constructs a function that tests for equality:
istwo = isequal(2)
istwo(2)
true
Sometimes, thinking about returning a function with “built-in” arguments can simplify your high-level code a lot!