Reading Functions
Operators
The operators are functions. For instance, you can use the addition operator (+
) in 2 ways:
3 + 2
+(3, 2)
The multiplication operator can be omitted when this does not create any ambiguity:
a = 3;
2a
Julia has "assignment by operation" operators:
a = 2;
a += 7 # this is the same as a = a + 7
There is a left division operator:
2\8 == 8/2
The boolean type is a subtype of the integer type:
Bool <: Integer
false == 0
true == 1
a = true
b = false
3a + 2b
Julia supports fraction operations:
4//8
1//2 + 3//4
Function definition
There are 2 ways to define a new function:
Long form
function <name>(<arguments>)
<body>
end
Example:
function hello()
println("Hello")
end
Assignment form
<name>(<arguments>) = <body>
Example:
hello() = println("Hello")
The function hello
defined with this compact syntax is exactly the same as the one we defined above with the longer syntax.
Calling functions
You call a function by running it followed by parentheses:
hello()
Arguments
Our function hello
does not accept any argument:
hello("Paul")
> ERROR: MethodError: no method matching hello(::String)
To define a function which accepts arguments, we need to add them in the function definition.
So maybe we could do this?
function hello(name)
println("Hello name")
end
hello("Paul")
Oops. Not quite… This function works but does not give the result we wanted.
Here, we need to use string interpolation with the $
character:
function hello(name)
println("Hello $name")
end
hello("Paul")
We can also set default argument values: if no arguments are given, the function is evaluated with the defaults.
function hello(name = "you")
println("Hello $name")
end
hello("Paul")
hello()
Here is another example:
function addTwo(a)
a + 2
end
addTwo(3)
# This can be written in a terse format with:
addtwo(a) = a + 2
# With default argument:
function addSomethingOrTwo(a, b = 2)
a + b
end
addSomethingOrTwo(3)
addSomethingOrTwo(3, 4)
Returning the result
The value of the last expression is automatically returned, so return
is unnecessary unless you want to return something else.
Look at these 5 functions:
function test1(x, y)
x + y
end
function test2(x, y)
return x + y
end
function test3(x, y)
x * y
end
function test4(x, y)
x * y
x + y
end
function test5(x, y)
return x * y
x + y
end
test1(1, 2)
test2(1, 2)
test3(1, 2)
test4(1, 2)
test5(1, 2)
Then run these expressions to see whether you got it right.
Anonymous functions
Anonymous functions are functions which aren't given a name:
function (<arguments>)
<body>
end
And in compact form:
<arguments> -> <body>
Example:
function (name)
println("Hello $name")
end
Compact form:
name -> println("Hello $name")
When would you want to use anonymous functions?
This is very useful for functional programming (when you apply a function—for instance map
—to other functions to apply them in a vectorized manner which avoids repetitions).
Example:
map(name -> println("Hello $name"), ["Paul", "Lucie", "Sophie"]);
Pipes
The Julia pipe looks like this: |>
.
It redirects the output of the expression on the left as the input of the expression on the right.
The following 2 expressions are thus equivalent:
println("Hello")
"Hello" |> println
Quick test:
sqrt(2) == 2 |> sqrt
Function composition
This is done with the composition operator ∘
(in the REPL, type \circ
then press <tab>
).
The following 2 expressions are equivalent:
<function2>(<function1>(<arguments>))
(<function2> ∘ <function1>)(<arguments>)
Example:
exp(+(-3, 1))
(exp ∘ +)(-3, 1)
function!()
!
used after a function name indicates that the function modifies its argument(s).
Example:
a = [-2, 3, -5]
sort(a)
a
sort!(a)
a
Broadcasting
To apply a function to each element of a collection rather than to the collection as a whole, Julia uses broadcasting.
a = [-3, 2, -5]
abs(a)
> ERROR: MethodError: no method matching abs(::Array{Int64,1})
This doesn't work because the function abs
only applies to single elements.
By broadcasting abs
, you apply it to each element of a
:
broadcast(abs, a)
The dot notation is equivalent:
abs.(a)
It can also be applied to the pipe, to unary and binary operators, etc.
a .|> abs
abs.(a) == a .|> abs
abs.(a) .== a .|> abs
Methods
Julia uses multiple dispatch: functions can have several methods. When that is the case, the method applied depends on the types of all the arguments passed to the function (rather than only the first argument as is common in other languages).
methods(+)
let's you see that +
has 166 methods!
Methods can be added to existing functions.
abssum(x::Int64, y::Int64) = abs(x + y)
abssum(x::Float64, y::Float64) = abs(x + y)
abssum(2, 4)
abssum(2.0, 4.0)
abssum(2, 4.0)