First steps#
Variables and expressions#
The assignment operator =
binds a name to a piece of data.
To see the data content:
@show expression
to show the expression and its resultprintln(data)
: good old print function.@printf
: C-like formatted output.Inline display: content of the last expression. A semicolon
;
at the end will suppress inline output.
An integer (64 bit)
x = 1
1
A (64-bit) floating-point number
y = 1.0
1.0
A 32-bit floating-point number. less accurate but calculates faster. Often used in GPU computing.
y = 1.0f0
1.0f0
Complex number
z = 3 + 5im
3 + 5im
Unicode names are supported. For example, \alpha<tab>
α = 3.74
3.74
Strings (Text) are surrounded by double quotes.
s = "Julia"
"Julia"
Characters are surrounded by single quotes.
c = ' '
' ': ASCII/Unicode U+0020 (category Zs: Separator, space)
Fractions (rational numbers)
station = 9 + 3//4
39//4
Constants will emit a warning if you try to change it after its creation
const theUltimateAnswer = 42
42
Print content to the terminal
println("Hello World")
Hello World
println("Hello ", s)
Hello Julia
println(1, 2, 3)
123
@show
will print x = val
@show x 1+1 2-2 3*3;
x = 1
1 + 1 = 2
2 - 2 = 0
3 * 3 = 9
Types of the data
@show typeof(x) typeof(y) typeof(c) typeof(s) typeof(z) typeof(1//2);
typeof(x) = Int64
typeof(y) = Float32
typeof(c) = Char
typeof(s) = String
typeof(z) = Complex{Int64}
typeof(1 // 2) = Rational{Int64}
convert(T,x)
converts x to type T
typeof(convert(Float64, x))
Float64
There is also Type(x)
typeof(Float64(x))
Float64
Converts numbers to floating numbers
typeof(float(x))
Float64
Math expressions#
Julia supports basic arithmetic operations and essential math functions by default.
Basic arithmetic#
Multiple assignment
a, b = 2, 3
(2, 3)
Addition
a + b
5
Subtraction
a - b
-1
Multiplication
a * b
6
Division
b / a
1.5
Fraction
b // a
3//2
integer division: \div<tab>
, the same as div(a, b)
a ÷ b
0
Modulus
b % a
1
Power
a^b
8
Comparison#
Returns a boolean value (true
or false
)
a, b = 2, 3
(2, 3)
a < 1
false
b > 2
true
a <= b
true
a != b
true
a == b + 1
false
Chained comparisons are supported
1 < a <= b
true
Approximation operator \approx<TAB>
for floating point number equivalence
1e10 + 1.0 ≈ 1e10
true
The same as
isapprox(1e10 + 1.0, 1e10)
true
Math functions#
How to type π : \pi<TAB>
sin(0.5*π)
1.0
More precise
sinpi(1//2)
1.0
cos(0.5*π)
6.123233995736766e-17
More precise
cospi(1//2)
0.0
\sqrt<TAB>
sqrt(π) ≈ √π
true
Natural log
log(10)
2.302585092994046
Common log
log10(10)
1.0
Natural exponent
exp(-5)
0.006737946999085467
expm1(x) is more accurate that exp(x) - 1 when x is very close to zero.
exp(1e-12) - 1, expm1(1e-12)
(1.000088900582341e-12, 1.0000000000005e-12)
Strings#
A string
is a sequence of characters.
" ... "
for one line strings.Three double quotes surround multiline string.
str1*str2*...
to concatenate stringsstring(str1, str2, ...)
to convert the data (if needed) and make a string.^
to repeat a string:str^3
to repeatstr
three times.[idx]
to access an individual character.$
to insert (or interpolate) a value into a string.
Although string(x, y)
looks less streamlined, it is generally faster than interpolation $
or concatenation *
.
"I am a string."
"I am a string."
"""
I am a multiline
string.
Hello Julia!
"""
"I am a multiline\nstring.\n\nHello Julia!\n"
A character is different from a string
'a' == "a"
false
How to insert contents into a string
str1 = "BEBI"
str2 = "5009"
string("The class is ", str1, '-', str2, ".")
"The class is BEBI-5009."
String interpolation $
"The class is $(str1)-$(str2)."
"The class is BEBI-5009."
String concatenation *
str1*"-"*str2
"BEBI-5009"
Compound expressions (Code blocks)#
A begin block
begin
…end
squashes multiple expressions into one.A let block
let
…end
is similar to a begin block but variables inside will be discarded outside the block.
a1 and a2 are available after begin block ends
begin
a1 = 2
a2 = 35
a1 = a1 * a2
end
70
x, y, z are NOT available after let block ends
let
x = 1
y = 2
z = 3
x + y * z
end
7
Control flow#
Conditional statements#
The
elseif
andelse
blocks are optional.if
andend
are mandatory.if
blocks return a value. Capturing the value is optional.if
blocks are “leaky”, i.e. they do not introduce a local scope. (The same as Python)Only boolean (true / false) could be evaluated in
if
blocks. Using other types (e.g. Int) will generate an error.
Ternary operator#
cond ? T:F
ifelse function#
A no-branching alternative. All the arguments are evaluated first in ifelse(cond, tvalue, fvalue)
.
short circuit evaluation#
&&
(logical and) and ||
(logical or) operators support short circuit evaluation.
In the expression
a && b
, the expressionb
would be evaluated only ifa
evaluates totrue
.In the expression
a || b
, the expressionb
would be evaluated only ifa
evaluates tofalse
.
Short circuit and &&
evaluates and returns the second argument if the first is true
(2 > 1) && println("Hi")
Hi
otherwise it returns false
(2 < 1) && println("Hi")
false
A if
block can return value(s)
let score = 10
response = if 80 < score <= 100
"Good"
elseif 60 < score <= 80
"Okay"
else
"Uh-Oh"
end
response
end
"Uh-Oh"
Ternary operator
1 > 2 ? "True" : "False"
"False"
Loops#
Repeated evaluations in a code block.
While loop (we will use it to solve stochastic simulations)
while cond
expr
end
For loop (we will use it to solve ODEs)
for i in seq
expr
end
Loop controls:
break
: exit the loop immediately.continue
: move on to the predicate immediately.
Hailstone sequence (3n+1 problem) in a while loop
let n = 1025489
step = 0
while n > 1
if iseven(n)
n ÷= 2
else
n = 3n + 1
end
step += 1
end
step
end
77
Summation
let n = 100
s = 0
for i in 1:n
s += i
end
s
end
5050
How continue
and break
work
for x in 1:9
if x == 5
continue ## jump to `x in 1:9`
elseif x >=8
break ## jump to the end of loop
end
println(x, "^2 = ", x^2)
end
1^2 = 1
2^2 = 4
3^2 = 9
4^2 = 16
6^2 = 36
7^2 = 49
You can use enumerate(seq)
to get a pair of index number and the element.
for (i, x) in enumerate([2, 3, 5, 7, 9, 11, 13])
println("xs[", i, "] = ", x)
end
xs[1] = 2
xs[2] = 3
xs[3] = 5
xs[4] = 7
xs[5] = 9
xs[6] = 11
xs[7] = 13
Multiple nested for loops can be combined into a single outer loop, forming the cartesian product of its iterables.
for i = 'x':'z', j = '1':'3'
println(i, j)
end
x1
x2
x3
y1
y2
y3
z1
z2
z3
Functions#
In Julia, a function is an object that maps a tuple of argument values to a return value. Julia docs
Functions could do:
Code reuse and encapsulation.
Specializations of Methods for different data types.
Notes:
Functions are first-class objects and can be passed into a higher-order function.
The arguments are “passed-by-sharing” (Similar to Python). Modifications to mutable argument values (such as
Arrays
) will be reflected to the caller.By convention, functions that will update the arguments are named with a bang
!
. (e.g.sort(arr)
vssort!(arr)
)For element-wise operations, use the broadcast (dot) syntax. e.g.
sqrt.(arr)
You can write multiple functions with the same name provided they have distinct parameters. Julia will choose the most appropriate one according to the input.
Standard form#
"Michaelis-Menton function" ## Function documentations
function mm(x, k) ## function name and parameter list
result = x / (x +k) ## Doing stuff
return result ## Return statement
end ## End of function
Main.var"##230".mm
Call the function
mm(1, 0.5)
0.6666666666666666
One-liner form#
f(x, y) = x + y
f(1, 2)
3
And you can also reuse previously-defined functions
mm(x) = mm(x, one(x))
mm(1)
0.5
Anonymous functions#
Anonymous functions are often used with other functions that take in another function. e.g. map(func, seq)
g = x->x^2
g(3)
9
map(x->x^2, 1:3)
3-element Vector{Int64}:
1
4
9
Use the do
block for long anonymous functions.
val = rand(-6:6, 10)
map(val) do x
res = if x < 0 && iseven(x)
zero(x)
elseif x == 0
one(x)
else
x
end
res
end
10-element Vector{Int64}:
6
6
0
1
3
4
0
5
4
0
The same as
map(x -> begin
res = if x < 0 && iseven(x)
zero(x)
elseif x == 0
one(x)
else
x
end
res
end, val)
Optional arguments#
Optional (positional) arguments are listed after mandatory ones.
function func(a, b, c=1)
## do_stuff
end
And they are called with func(a, b)
or func(a, b, 3)
Keyword arguments#
Keyword arguments are listed after ;
. They are called by name rather than order.
function plot(x, y; style="solid", width=1, color="black")
## do_stuff
end
And they are called with plot(x, y, width=2)
or plot(x, y; width=2)
See also#
Collections#
Using built-in collections is the simplest way to group and organize data.
The values in a immutable
collection cannot be updated after its creation, while in a mutable
collection can.
The elements in sequential
collections are accessed by integer indices, while those in associative
collection are accessed by keys.
Sequential collections#
General rules for sequential collections:
1-based indexing, as in R, MATLAB, and Fortran.
Elements are accessed by an integer index
seq[i]
or an integer rangeseq[1:2:end-1]
.length(seq)
returns the total sizeSplat operator
...
passes the inner contents in the collection as positional function arguments.Dot syntax (e.g. a .+ b) performs element-wise / broadcasting operations.
Ranges#
start[:step]:end
Immutable
Sequential
Cheap
Evenly-spaced numerical sequences
A simple range starts from one, and stops at ten, with a step size of two,.
1:2:10
1:2:9
Length of a sequence
length(1:2:10)
5
Show its content
dump(1:2:10)
StepRange{Int64, Int64}
start: Int64 1
step: Int64 2
stop: Int64 9
Explicit range function
range(1, 10, length=10)
1.0:1.0:10.0
Pick an element
(1:10)[3]
3
Pick elements by a range of indices
(1:10)[3:end]
3:10
Tuples#
immutable
sequential collections
efficient for heterogenous data of various types
stack-allocated
tuple(1, 'a', 3.14)
(1, 'a', 3.14)
Tuples are usually written as
(1, 'a', 3.14)
(1, 'a', 3.14)
Pick elements You cannot change the elements once its created. (immutable)
t1 = (1, 2, 3)
t1[1]
1
t2 = (1, 'a', 3.14)
dump(t2)
Tuple{Int64, Char, Float64}
1: Int64 1
2: Char 'a'
3: Float64 3.14
Merging multiple tuples using the splat (…) operator
tuple(t1..., t2...)
# Tuples could be used to swap elements
let x = 1, y = 2, z = 3
x, y, z = z, x, y
@show x, y, z
end;
(x, y, z) = (3, 1, 2)
Tuple can return multiple values from a function
neighbors(x) = x+1, x-1
neighbors(0)
(1, -1)
extrema([1, 5, 6, 7, -1, -3, 0])
(-3, 7)
sincospi(1//2)
(1.0, 0.0)
Arrays#
[seq...]
/ collect(seq)
Arrays are the bread and butter for scientific computing, similar to numpy’s ndarrays
.
Column-major (Fortran style) rather than row-major (C and numpy style)
Assignments and updating may cause unwanted editing due to memory sharing.
Some useful functions for arrays:
length(A)
the number of elements in Andims(A)
the number of dimensions of Asize(A)
a tuple containing the dimensions of Asize(A,n)
the size of A along dimension neachindex(A)
an efficient iterator for visiting each position in A
1D array (column vector)
x = [5, 6, 7]
3-element Vector{Int64}:
5
6
7
np.arange() equivalent
collect(1:10)
10-element Vector{Int64}:
1
2
3
4
5
6
7
8
9
10
Array with all zeroes
zeros(2, 5, 2)
2×5×2 Array{Float64, 3}:
[:, :, 1] =
0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0
[:, :, 2] =
0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 0.0 0.0
Array with all ones
ones(2, 5)
2×5 Matrix{Float64}:
1.0 1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0 1.0
Uninitialized array with the same data type and dims as x
similar(x)
3-element Vector{Int64}:
129394953697408
129394953690688
129394953752832
np.zeros_like()
zero(x)
3-element Vector{Int64}:
0
0
0
Array of random numbers
rand(1:6, 10)
10-element Vector{Int64}:
2
2
6
6
2
6
6
3
3
5
rand(1:6, 2, 2)
2×2 Matrix{Int64}:
6 1
6 5
Reshape an array
reshape(1:12, 3, 4)
3×4 reshape(::UnitRange{Int64}, 3, 4) with eltype Int64:
1 4 7 10
2 5 8 11
3 6 9 12
Reshape A to an (1D) vector
vec(rand(1:6, 2, 2))
4-element Vector{Int64}:
3
5
3
4
repeat the array 3x2 times
repeat([1 2; 3 4], 3, 2)
6×4 Matrix{Int64}:
1 2 1 2
3 4 3 4
1 2 1 2
3 4 3 4
1 2 1 2
3 4 3 4
comprehension for 1D array
[i^2 for i in 1:10 if i >= 5]
6-element Vector{Int64}:
25
36
49
64
81
100
2D comprehension for a 2x3 array
[x * y for x in 1:2, y in 1:3]
2×3 Matrix{Int64}:
1 2 3
2 4 6
casting comprehension result element type to Float64
Float64[x^2 for x in 1:4]
4-element Vector{Float64}:
1.0
4.0
9.0
16.0
This is a 1-element tuple containing a vector
tuple([1,2,3])
([1, 2, 3],)
How to convert vector to tuple
Tuple([1,2,3])
(1, 2, 3)
2D array (matrix) A space is a shorthand for hcat() A semicolon is a shorthand for vcat()
A = [1 2 3;
4 5 6]
2×3 Matrix{Int64}:
1 2 3
4 5 6
Accessing elements
A[1, 2]
2
Accessing a range of elements
A[1:2, 2:3]
2×2 Matrix{Int64}:
2 3
5 6
Array total length
length(A)
6
axes(A)
(Base.OneTo(2), Base.OneTo(3))
size(A)
(2, 3)
ndims(A)
2
transpose(A)
3×2 transpose(::Matrix{Int64}) with eltype Int64:
1 4
2 5
3 6
(Conjugate transpose) Adjoint
A'
3×2 adjoint(::Matrix{Int64}) with eltype Int64:
1 4
2 5
3 6
Matrix-vector multiplication
b = A * x
2-element Vector{Int64}:
38
92
Find x for Ax = b, using left division operator /
A\b ≈ x
true
Flatten A to an (1D) vector
vec(A)
6-element Vector{Int64}:
1
4
2
5
3
6
Arrays are mutable (i.e. you can update the contents) objects You should make a copy if you want the original one intact
A[1, 1] = 0
A
2×3 Matrix{Int64}:
0 2 3
4 5 6
Associative collections#
d[key]
accesses values by keysd[key] = value
sets a key-value pair for a mutable dictionary.delete!(d, key)
deletes the kay (and its partner) from a mutable dictionary.keys(d)
returns a series of keysvalues(d)
returns a series of valuespairs(d)
returns a series of (key => value) pairsmerge(d1, d2, ...)
return combinations of several dicts.merge!(d1, d2, ...)
combine several dicts and update the first one.get(d, key, default)
returns the value stored for the given key, or the given default value if no mapping for the key is present.
Named tuples#
Namedtuple
s are tuples with key-value pairs.
nt = (a=1, b=2, c=4)
(a = 1, b = 2, c = 4)
nt[1]
1
nt.a == nt[:a] == nt[1]
true
How to fill a named tuple elegantly
a = 1
b = 2
c = 3
nt = (; a, b, c)
(a = 1, b = 2, c = 3)
Dictionaries#
Dictionaries are mutable mappings of key => value
.
eng2sp = Dict("one" => "uno", "two" => "dos", "three" => "tres")
Dict{String, String} with 3 entries:
"two" => "dos"
"one" => "uno"
"three" => "tres"
eng2sp["two"]
"dos"
eng2sp["five"] = "cinco"
"cinco"
keys(eng2sp)
KeySet for a Dict{String, String} with 4 entries. Keys:
"two"
"one"
"three"
"five"
values(eng2sp)
ValueIterator for a Dict{String, String} with 4 entries. Values:
"dos"
"uno"
"tres"
"cinco"
get(eng2sp, "one", "N/A")
"uno"
get(eng2sp, "four", "N/A")
"N/A"
haskey(eng2sp, "one")
true
Elements are not ordered
for (k ,v) in eng2sp
println(k, " => ", v)
end
two => dos
one => uno
three => tres
five => cinco
Creating a dict from an array of tuples
Dict([('a', 1), ('c', 3), ('b', 2)])
Dict{Char, Int64} with 3 entries:
'a' => 1
'c' => 3
'b' => 2
Creating a Dict via a generator (similar to comprehensions)
Dict(i => i^2 for i = 1:10)
Dict{Int64, Int64} with 10 entries:
5 => 25
4 => 16
6 => 36
7 => 49
2 => 4
10 => 100
9 => 81
8 => 64
3 => 9
1 => 1
Dict(zip("abc", 1:3))
Dict{Char, Int64} with 3 entries:
'a' => 1
'c' => 3
'b' => 2
Broadcasting (Dot) syntax#
Broadcasting turns scalar operations into vector ones.
[1, 2, 3] .+ [4, 5, 6]
3-element Vector{Int64}:
5
7
9
[1, 2, 3] .+ 4
3-element Vector{Int64}:
5
6
7
Element-wise operation
sinpi.([0.5, 1.0, 1.5, 2.0])
4-element Vector{Float64}:
1.0
0.0
-1.0
0.0
Create a dictionary with a list of keys and a list of values
ks = (:a, :b, :c)
vs = (1, 2, 3)
Dict(ks .=> vs)
Dict{Symbol, Int64} with 3 entries:
:a => 1
:b => 2
:c => 3
How to do logspace()
in Julia
exp10.(range(-3.0, 3.0, 50))
50-element Vector{Float64}:
0.001
0.0013257113655901094
0.0017575106248547913
0.002329951810515372
0.0030888435964774815
0.004094915062380423
0.005428675439323859
0.0071968567300115215
0.009540954763499945
0.012648552168552958
⋮
104.81131341546852
138.94954943731375
184.20699693267164
244.20530945486522
323.74575428176433
429.1934260128778
568.9866029018299
754.3120063354615
1000.0
Make a 9*9 multiplication table
collect(1:9) .* transpose(collect(1:9))
9×9 Matrix{Int64}:
1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
5 10 15 20 25 30 35 40 45
6 12 18 24 30 36 42 48 54
7 14 21 28 35 42 49 56 63
8 16 24 32 40 48 56 64 72
9 18 27 36 45 54 63 72 81
Custom data structures and Methods#
https://docs.julialang.org/en/v1/manual/types/#Composite-Types
struct or mutable struct
struct Point
x
y
end
Define a default constructor
Point() = Point(0.0, 0.0)
Main.var"##230".Point
p1 = Point(1.0, 2.0)
p2 = Point(-3.0, 2.0)
Main.var"##230".Point(-3.0, 2.0)
Define a method for our custom type
add(a::Point, b::Point) = Point(a.x + b.x, a.y + b.y)
add (generic function with 1 method)
add(p1, p2)
Main.var"##230".Point(-2.0, 4.0)
Methods#
You can overload the same function with different argument types/numbers. Julia will try to find the right function for the argument type(s).
func(a::Int) = a + 2
func(a::AbstractFloat) = a/2
func(a::Rational) = a//11
func(a::Complex) = sqrt(a)
func(a, b::String) = "$a, $b"
func (generic function with 5 methods)
func(1)
3
func(3.0)
1.5
func(33//4)
3//4
func(-2 + 0im)
0.0 + 1.4142135623730951im
func(true, "it just works")
"true, it just works"
This notebook was generated using Literate.jl.
Comments#
Comments are non-coding parts in the source code. Although the compiler does not read comments, comments are important notes for humans, making the code more readable (hopefully).