Introduction to the Julia programming language
7 Functions¶
Functions¶
Functions are defined in a very straightforward way using the function ... end
syntax:
function mymult(a, b)
a * b
end
mymult (generic function with 1 method)
mymult(3, 7)
21
mymult(6+3im, -9-7im)
-33 - 69im
mymult("gold", "leader")
"goldleader"
Notice our function is working for any types (we used Int64
, Complex
and String
) - this stays fast in Julia because of multiple dispatch that we discuss later.
Function Documentation¶
A string directly before a function definition is interpreted by Julia as "documentation" for the function.
The ?
operator will display the doc strings for a function, amongst other things (? also provides help for expressions and other constructs). All of the Julia standard library functions have docstrings.
"""
adder(a, b)
This function adds `a` and `b`
"""
function adder(a, b)
a+b
end
adder
adder(1,2)
3
Compact Function Definition¶
If we can write a function on one line then Julia allows us to not bother with function ... end
:
mypow(a, b) = a^b
mypow (generic function with 1 method)
mypow(2, 8)
256
mypow("old ", 4)
"old old old old "
Anonymous Functions (lambdas)¶
Anonymous functions in Julia are written using ->
, like this:
x -> x>2
#11 (generic function with 1 method)
Which is not too useful like that, but comes into its own when used with something like map
:
map(x -> x>2, 1:5)
5-element Vector{Bool}: 0 0 1 1 1
A further example of an anonymous function:
((x,y) -> x + y)(1,2)
3
Optional Arguments¶
opt1(x, y=2, z=2y) = x*y*z
@show opt1(1)
@show opt1(1, 2)
@show opt1(1, 2, 4);
opt1(1) = 8 opt1(1, 2) = 8 opt1(1, 2, 4) = 8
Keyword Arguments¶
Define a function that takes both positional and keyword arguments:
function greet(name::String; greeting="Hello", punctuation="!")
return "$greeting, $name$punctuation"
end
# Call the function with only the required positional argument
println(greet("Alice"))
# Call the function with a specified greeting keyword argument
println(greet("Bob", greeting="Good morning"))
# Call the function with both greeting and punctuation specified
println(greet("Charlie", greeting="Hi", punctuation="."))
Hello, Alice! Good morning, Bob! Hi, Charlie.
When the function is called, the semicolon is optional: one can either call greet("Bob", greeting="Good morning") or plot("Bob"; greeting="Good morning"), but the former style is more common.
Varargs Functions¶
It is often convenient to be able to write functions taking an arbitrary number of arguments. Such functions are traditionally known as "varargs" functions, which is short for "variable number of arguments". You can define a varargs function by following the last positional argument with an ellipsis:
bar(a,b,x...) = (a,b,x)
bar (generic function with 1 method)
bar(1,2)
(1, 2, ())
bar(1, 2, 3, 4)
(1, 2, (3, 4))
In all these cases, x
is bound to a tuple of the trailing values passed to bar
.
Arguments are passed by reference¶
Change A at index i to x
Convention to add ! for functions that change the inputs
function setvalue!(A, x, i...)
A[i...] = x
end
v = randn(2,2)
setvalue!(v, 1.0, 1, 2)
v
2×2 Matrix{Float64}: -0.508663 1.0 -0.3267 -1.79433
Recursion¶
function fact(n)
if n == 0
return 1
else
return n * fact(n-1)
end
end
fact(5)
120
Do-block syntax¶
The do-block
syntax helps when your anonymous function is too long but doesn't deserve its own name.
This:
function _one_off(x)
if x>=1
return 1.0
elseif x<0
return -1.0
else
return NaN
end
end
map(_one_off, -1:1)
3-element Vector{Float64}: -1.0 NaN 1.0
is equivalent to writing the following do-block
:
map(-1:1) do x
if x>=1
return 1.0
elseif x<0
return -1.0
else
return NaN
end
end
3-element Vector{Float64}: -1.0 NaN 1.0
More throughly speaking, the do-block
syntax makes an anonymous function using the body (between do
and end
), taking variable names immediately after the do
as parameters, and passes this function as the first argument of map()
. Note, this works on any function (that expects first argument to be callable), not just map()
.
Many functions in the Julia standard library have alternate forms which take a function as their first argument to allow this kind of style. Generally, such functions also perform clean-up after the do-block has completed, making this a lot like with
blocks in Python.
For example, there is a version of open
which enables a very familiar form to Python programmers:
open("somefile.txt", write=true) do io
write(io, "I added this line!\n")
end
19
Operators Are Functions¶
+(1,2,3)
6
f = +;
f(1, 2, 3)
6
Function composition and piping¶
Functions in Julia can be combined by composing or piping (chaining) them together.
Function composition is when you combine functions together and apply the resulting composition to arguments. You use the function composition operator (∘) to compose the functions, so (f ∘ g)(args...) is the same as f(g(args...)).
You can type the composition operator at the REPL and suitably-configured editors using \circ
For example, the sqrt and + functions can be composed like this:
(sqrt ∘ +)(3, 6)
3.0
Function chaining (sometimes called "piping" or "using a pipe" to send data to a subsequent function) is when you apply a function to the previous function's output:
1:10 |> sum |> sqrt
7.416198487095663