Julia Language
Julia Language
#julia-lang
Table of Contents
About 1
Versions 2
Examples 2
Hello, World! 2
Syntax 4
Remarks 4
Examples 4
Input validation 4
Error cleanup 5
Chapter 3: Arithmetic 6
Syntax 6
Examples 6
Quadratic Formula 6
Sieve of Eratosthenes 6
Matrix Arithmetic 7
Sums 7
Products 8
Powers 8
Chapter 4: Arrays 10
Syntax 10
Parameters 10
Examples 10
Array types 11
Vectors 13
Concatenation 14
Horizontal Concatenation 14
Vertical Concatenation 15
Chapter 5: Closures 17
Syntax 17
Remarks 17
Examples 17
Function Composition 17
Implementing Currying 18
Introduction to Closures 19
Chapter 6: Combinators 21
Remarks 21
Examples 21
The Y or Z Combinator 21
Chapter 7: Comparisons 25
Syntax 25
Remarks 25
Examples 25
Chained Comparisons 25
Ordinal Numbers 27
Standard Operators 28
When to use == 29
Chapter 8: Comprehensions 33
Examples 33
Array comprehension 33
Basic Syntax 33
Conditional Array Comprehension 33
Generator Comprehensions 34
Function Arguments 35
Chapter 9: Conditionals 36
Syntax 36
Remarks 36
Examples 36
if...else expression 36
if...else statement 37
if statement 37
For branching 38
In conditions 38
Syntax 41
Remarks 41
Examples 41
Version numbers 41
Using Compat.jl 42
Examples 44
Using Dictionaries 44
Syntax 45
Remarks 45
Examples 45
Defining an enumerated type 45
Examples 48
Intro to Expressions 48
Creating Expressions 48
Syntax 54
Remarks 54
Examples 54
Fizz Buzz 54
Multidimensional iteration 55
Syntax 57
Remarks 57
Examples 57
Square a number 57
Recursive functions 58
Simple recursion 58
Introduction to Dispatch 58
Optional Arguments 59
Parametric Dispatch 60
Imperative factorial 62
Anonymous functions 63
Arrow syntax 63
Multiline syntax 63
Do block syntax 64
Syntax 65
Remarks 65
Examples 65
Functions as arguments 65
Syntax 68
Parameters 68
Examples 68
Syntax 74
Parameters 74
Examples 74
Lazily-Evaluated Lists 78
Syntax 80
Remarks 80
Examples 80
Installing JSON.jl 80
Parsing JSON 80
Serializing JSON 81
Syntax 82
Remarks 82
Examples 82
Until loop 83
Guide 89
Symbol 90
Expr (AST) 91
macro s 93
esc() 95
ADVANCED 99
Scott's macro: 99
??? 104
Introduction 105
Usage 106
Misusage 107
Syntax 108
Examples 108
Syntax 110
Parameters 110
Examples 110
Examples 113
pmap 113
@parallel 113
Examples 123
Reading a dataframe from delimiter separated data 123
Syntax 124
Parameters 124
Examples 124
Syntax 127
Remarks 127
Examples 127
On Windows 127
Syntax 132
Examples 132
Syntax 133
Remarks 133
Examples 133
@big_str 134
@doc_str 134
@html_str 135
@ip_str 135
@r_str 135
@s_str 136
@text_str 136
@v_str 136
@MIME_str 136
Syntax 140
Parameters 140
Examples 140
Syntax 142
Parameters 142
Examples 142
Graphemes 143
Parameters 147
Remarks 147
Examples 147
Syntax 149
Examples 149
Syntax 151
Remarks 151
Examples 151
Introduction 157
Examples 157
Syntax 158
Remarks 158
Examples 158
Syntax 163
Remarks 163
Examples 163
Syntax 170
Remarks 170
Examples 170
Credits 174
About
You can share this PDF with anyone you feel could benefit from it, downloaded the latest version
from: julia-language
It is an unofficial and free Julia Language ebook created for educational purposes. All the content
is extracted from Stack Overflow Documentation, which is written by many hardworking individuals
at Stack Overflow. It is neither affiliated with Stack Overflow nor official Julia Language.
The content is released under Creative Commons BY-SA, and the list of contributors to each
chapter are provided in the credits section at the end of this book. Images may be copyright of
their respective owners unless otherwise specified. All trademarks and registered trademarks are
the property of their respective company owners.
Use the content presented in this book at your own risk; it is not guaranteed to be correct nor
accurate, please send your feedback and corrections to [email protected]
https://riptutorial.com/ 1
Chapter 1: Getting started with Julia
Language
Versions
0.6.0-dev 2017-06-01
0.5.0 2016-09-19
0.4.0 2015-10-08
0.3.0 2014-08-21
0.2.0 2013-11-17
0.1.0 2013-02-14
Examples
Hello, World!
println("Hello, World!")
To run Julia, first get the interpreter from the website’s download page. The current stable release
is v0.5.0, and this version is recommended for most users. Certain package developers or power
users may choose to use the nightly build, which is far less stable.
When you have the interpreter, write your program in a file named hello.jl. It can then be run
from a system terminal as:
$ julia hello.jl
Hello, World!
Julia can also be run interactively, by running the julia program. You should see a header and
prompt, as follows:
_
_ _ _(_)_ | A fresh approach to technical computing
(_) | (_) (_) | Documentation: http://docs.julialang.org
_ _ _| |_ __ _ | Type "?help" for help.
| | | | | | |/ _` | |
| | |_| | | | (_| | | Version 0.4.2 (2015-12-06 21:47 UTC)
_/ |\__'_|_|_|\__'_| | Official http://julialang.org/ release
https://riptutorial.com/ 2
|__/ | x86_64-w64-mingw32
julia>
This example makes use of a string, "Hello, World!", and of the println function—one of many in
the standard library. For more information or help, try the following sources:
https://riptutorial.com/ 3
Chapter 2: @goto and @label
Syntax
• @goto label
• @label label
Remarks
Overuse or inappropriate use of advanced control flow makes code hard to read. @goto or its
equivalents in other languages, when used improperly, leads to unreadable spaghetti code.
Similar to languages like C, one cannot jump between functions in Julia. This also means that
@goto is not possible at the top-level; it will only work within a function. Furthermore, one cannot
jump from an inner function to its outer function, or from an outer function to an inner function.
Examples
Input validation
Although not traditionally considered loops, the @goto and @label macros can be used for more
advanced control flow. One use case is when the failure of one part should lead to the retry of an
entire function, often useful in input validation:
function getsequence()
local a, b
@label start
print("Input an integer: ")
try
a = parse(Int, readline())
catch
println("Sorry, that's not an integer.")
@goto start
end
a, b
end
https://riptutorial.com/ 4
function getsequence()
local a, b
a, b
end
Although both examples do the same thing, the second is easier to understand. However, the first
one is more performant (because it avoids the recursive call). In most cases, the cost of the call
does not matter; but in limited situations, the first form is acceptable.
Error cleanup
In languages such as C, the @goto statement is often used to ensure a function cleans up
necessary resources, even in the event of an error. This is less important in Julia, because
exceptions and try-finally blocks are often used instead.
However, it is possible for Julia code to interface with C code and C APIs, and so sometimes
functions still need to be written like C code. The below example is contrived, but demonstrates a
common use case. The Julia code will call Libc.malloc to allocate some memory (this simulates a
C API call). If not all allocations succeed, then the function should free the resources obtained so
far; otherwise, the allocated memory is returned.
using Base.Libc
function allocate_some_memory()
mem1 = malloc(100)
mem1 == C_NULL && @goto fail
mem2 = malloc(200)
mem2 == C_NULL && @goto fail
mem3 = malloc(300)
mem3 == C_NULL && @goto fail
return mem1, mem2, mem3
@label fail
free(mem1)
free(mem2)
free(mem3)
end
https://riptutorial.com/ 5
Chapter 3: Arithmetic
Syntax
• +x
• -x
• a+b
• a-b
• a*b
• a/b
• a^b
• a%b
• 4a
• sqrt(a)
Examples
Quadratic Formula
Julia uses similar binary operators for basic arithmetic operations as does mathematics or other
programming languages. Most operators can be written in infix notation (that is, placed in between
the values being computed). Julia has an order of operations that matches the common
convention in mathematics.
For instance, the below code implements the quadratic formula, which demonstrates the +, -, *,
and / operators for addition, subtraction, multiplication, and division respectively. Also shown is
implicit multiplication, where a number can be placed directly before a symbol to mean
multiplication; that is, 4a means the same as 4*a.
function solvequadratic(a, b, c)
d = sqrt(b^2 - 4a*c)
(-b - d) / 2a, (-b + d) / 2a
end
Usage:
Sieve of Eratosthenes
The remainder operator in Julia is the % operator. This operator behaves similarly to the % in
languages such as C and C++. a % b is the signed remainder left over after dividing a by b.
This operator is very useful for implementing certain algorithms, such as the following
https://riptutorial.com/ 6
implementation of the Sieve of Eratosthenes.
function sieve(n)
P = Int[]
for i in 2:n
if iscoprime(P, i)
push!(P, i)
end
end
P
end
Usage:
julia> sieve(20)
8-element Array{Int64,1}:
2
3
5
7
11
13
17
19
Matrix Arithmetic
Julia uses the standard mathematical meanings of arithmetic operations when applied to matrices.
Sometimes, elementwise operations are desired instead. These are marked with a full stop (.)
preceding the operator to be done elementwise. (Note that elementwise operations are often not
as efficient as loops.)
Sums
The + operator on matrices is a matrix sum. It is similar to an elementwise sum, but it does not
broadcast shape. That is, if A and B are the same shape, then A + B is the same as A .+ B;
otherwise, A + B is an error, whereas A .+ B may not necessarily be.
julia> A = [1 2
3 4]
2×2 Array{Int64,2}:
1 2
3 4
julia> B = [5 6
7 8]
2×2 Array{Int64,2}:
5 6
7 8
julia> A + B
2×2 Array{Int64,2}:
https://riptutorial.com/ 7
6 8
10 12
julia> A .+ B
2×2 Array{Int64,2}:
6 8
10 12
julia> A + C
ERROR: DimensionMismatch("dimensions must match")
in promote_shape(::Tuple{Base.OneTo{Int64},Base.OneTo{Int64}}, ::Tuple{Base.OneTo{Int64}}) at
./operators.jl:396
in promote_shape(::Array{Int64,2}, ::Array{Int64,1}) at ./operators.jl:382
in _elementwise(::Base.#+, ::Array{Int64,2}, ::Array{Int64,1}, ::Type{Int64}) at
./arraymath.jl:61
in +(::Array{Int64,2}, ::Array{Int64,1}) at ./arraymath.jl:53
julia> A .+ C
2×2 Array{Int64,2}:
10 11
13 14
Likewise, - computes a matrix difference. Both + and - can also be used as unary operators.
Products
The * operator on matrices is the matrix product (not the elementwise product). For an
elementwise product, use the .* operator. Compare (using the same matrices as above):
julia> A * B
2×2 Array{Int64,2}:
19 22
43 50
julia> A .* B
2×2 Array{Int64,2}:
5 12
21 32
Powers
The ^ operator computes matrix exponentiation. Matrix exponentiation can be useful for computing
values of certain recurrences quickly. For instance, the Fibonacci numbers can be generated by
the matrix expression
As usual, the .^ operator can be used where elementwise exponentiation is the desired operation.
https://riptutorial.com/ 8
Read Arithmetic online: https://riptutorial.com/julia-lang/topic/3848/arithmetic
https://riptutorial.com/ 9
Chapter 4: Arrays
Syntax
• [1,2,3]
• [1 2 3]
• [1 2 3; 4 5 6; 7 8 9]
• Array(type, dims...)
• ones(type, dims...)
• zeros(type, dims...)
• trues(type, dims...)
• falses(type, dims...)
• push!(A, x)
• pop!(A)
• unshift!(A, x)
• shift!(A)
Parameters
Parameters Remarks
Examples
Manual construction of a simple array
One can initialize a Julia array by hand, using the square-brackets syntax:
julia> x = [1, 2, 3]
3-element Array{Int64,1}:
1
2
3
The first line after the command shows the size of the array you created. It also shows the type of
its elements and its dimensionality (int this case Int64 and 1, repectively). For a two-dimensional
array, you can use spaces and semi-colon:
julia> x = [1 2 3; 4 5 6]
2x3 Array{Int64,2}:
https://riptutorial.com/ 10
1 2 3
4 5 6
To create an uninitialized array, you can use the Array(type, dims...) method:
julia> Array(Int64, 3, 3)
3x3 Array{Int64,2}:
0 0 0
0 0 0
0 0 0
The functions zeros, ones, trues, falses have methods that behave exactly the same way, but
produce arrays full of 0.0, 1.0, True or False, respectively.
Array types
In Julia, Arrays have types parametrized by two variables: a type T and a dimensionality D (
Array{T, D}). For a 1-dimensional array of integers, the type is:
julia> x = [1 2 3; 4 5 6; 7 8 9]
julia> typeof(x)
Array{Int64, 2}
julia> x = [1 2 3; 4 5 "6"; 7 8 9]
3x3 Array{Any,2}:
1 2 3
4 5 "6"
7 8 9
Here Any (an abstract type) is the type of the resulting array.
When we create an Array in the way described above, Julia will do its best to infer the proper type
that we might want. In the initial examples above, we entered inputs that looked like integers, and
so Julia defaulted to the default Int64 type. At times, however, we might want to be more specific.
In the following example, we specify that we want the type to be instead Int8:
x1 = Int8[1 2 3; 4 5 6; 7 8 9]
typeof(x1) ## Array{Int8,2}
We could even specify the type as something such as Float64, even if we write the inputs in a way
https://riptutorial.com/ 11
that might otherwise be interpreted as integers by default (e.g. writing 1 instead of 1.0). e.g.
x2 = Float64[1 2 3; 4 5 6; 7 8 9]
In Julia, you can have an Array that holds other Array type objects. Consider the following
examples of initializing various types of Arrays:
rand(3) produces an object of type Array{Float64,1}. Since the only specification for the elements
of C are that they be Arrays with elements of type Float64, this fits within the definition of C. But, for
D we specified that the elements must be 2 dimensional Arrays. Thus, since rand(3) does not
produce a 2 dimensional array, we cannot use it to assign a value to a specific element of D
Although we can specify that an Array will hold elements which are of type Array, and we can
specify that, e.g. those elements should be 2-dimensional Arrays, we cannot directly specify the
dimenions of those elements. E.g. we can't directly specify that we want an Array holding 10
Arrays, each of which being 5,5. We can see this from the syntax for the Array() function used to
construct an Array:
Array{T}(dims)
constructs an uninitialized dense array with element type T. dims may be a tuple or a
series of integer arguments. The syntax Array(T, dims) is also available, but
deprecated.
The type of an Array in Julia encompasses the number of the dimensions but not the size of those
https://riptutorial.com/ 12
dimensions. Thus, there is no place in this syntax to specify the precise dimensions. Nevertheless,
a similar effect could be achieved using an Array comprehension:
We can use the [] to create an empty Array in Julia. The simplest example would be:
A = [] # 0-element Array{Any,1}
Arrays of type Any will generally not perform as well as those with a specified type. Thus, for
instance, we can use:
See Initialize an Empty Array of Tuples in Julia for source of last example.
Vectors
Vectors are one-dimensional arrays, and support mostly the same interface as their multi-
dimensional counterparts. However, vectors also support additional operations.
First, note that Vector{T} where T is some type means the same as Array{T,1}.
julia> Vector{Int}
Array{Int64,1}
julia> Vector{Float64}
Array{Float64,1}
Unlike multi-dimensional arrays, vectors can be resized. Elements can be added or removed from
the front or back of the vector. These operations are all constant amortized time.
julia> A = [1, 2, 3]
3-element Array{Int64,1}:
1
2
3
julia> push!(A, 4)
4-element Array{Int64,1}:
1
2
3
https://riptutorial.com/ 13
4
julia> A
4-element Array{Int64,1}:
1
2
3
4
julia> pop!(A)
4
julia> A
3-element Array{Int64,1}:
1
2
3
julia> unshift!(A, 0)
4-element Array{Int64,1}:
0
1
2
3
julia> A
4-element Array{Int64,1}:
0
1
2
3
julia> shift!(A)
0
julia> A
3-element Array{Int64,1}:
1
2
3
As is convention, each of these functions push!, pop!, unshift!, and shift! ends in an exclamation
mark to indicate that they are mutate their argument. The functions push! and unshift! return the
array, whereas pop! and shift! return the element removed.
Concatenation
Horizontal Concatenation
Matrices (and vectors, which are treated as column vectors) can be horizontally concatenated
using the hcat function.
https://riptutorial.com/ 14
1 2 5 6 7 11
3 4 8 9 10 12
There is convenience syntax available, using square bracket notation and spaces:
This notation can closely match the notation for block matrices used in linear algebra:
julia> A = [1 2; 3 4]
2×2 Array{Int64,2}:
1 2
3 4
julia> B = [5 6; 7 8]
2×2 Array{Int64,2}:
5 6
7 8
julia> [A B]
2×4 Array{Int64,2}:
1 2 5 6
3 4 7 8
Note that you cannot horizontally concatenate a single matrix using the [] syntax, as that would
instead create a one-element vector of matrices:
julia> [A]
1-element Array{Array{Int64,2},1}:
[1 2; 3 4]
Vertical Concatenation
Vertical concatenation is like horizontal concatenation, but in the vertical direction. The function for
vertical concatenation is vcat.
Alternatively, square bracket notation can be used with semicolons ; as the delimiter:
https://riptutorial.com/ 15
1 2
3 4
5 6
7 8
9 10
11 12
julia> A = [1, 2, 3]
3-element Array{Int64,1}:
1
2
3
julia> B = [4, 5]
2-element Array{Int64,1}:
4
5
julia> [A; B]
5-element Array{Int64,1}:
1
2
3
4
5
julia> A = [1 2
3 4]
2×2 Array{Int64,2}:
1 2
3 4
julia> B = [5 6 7]
1×3 Array{Int64,2}:
5 6 7
julia> C = [8, 9]
2-element Array{Int64,1}:
8
9
julia> [A C; B]
3×3 Array{Int64,2}:
1 2 8
3 4 9
5 6 7
https://riptutorial.com/ 16
Chapter 5: Closures
Syntax
• x -> [body]
• (x, y) -> [body]
• (xs...) -> [body]
Remarks
0.4.0
In older versions of Julia, closures and anonymous functions had a runtime performance penalty.
This penalty has been eliminated in 0.5.
Examples
Function Composition
We can define a function to perform function composition using anonymous function syntax:
f ∘ g = x -> f(g(x))
or
function ∘(f, g)
x -> f(g(x))
end
julia> double(x) = 2x
double (generic function with 1 method)
julia> triple(x) = 3x
triple (generic function with 1 method)
julia> sextuple(1.5)
https://riptutorial.com/ 17
9.0
0.5.0
In version v0.5, this definition is very performant. We can look into the LLVM code generated:
It is clear that the two multiplications have been folded into a single multiplication, and that this
function is as efficient as is possible.
How does this higher-order function work? It creates a so-called closure, which consists of not just
its code, but also keeps track of certain variables from its scope. All functions in Julia that are not
created at top-level scope are closures.
0.5.0
One can inspect the variables closed over through the fields of the closure. For instance, we see
that:
Implementing Currying
One application of closures is to partially apply a function; that is, provide some arguments now
and create a function that takes the remaining arguments. Currying is a specific form of partial
application.
Let's start with the simple function curry(f, x) that will provide the first argument to a function, and
expect additional arguments later. The definition is fairly straightforward:
Once again, we use anonymous function syntax, this time in combination with variadic argument
syntax.
We can implement some basic functions in tacit (or point-free) style using this curry function.
https://riptutorial.com/ 18
julia> double(10)
20
Introduction to Closures
Functions are an important part of Julia programming. They can be defined directly within
modules, in which case the functions are referred to as top-level. But functions can also be defined
within other functions. Such functions are called "closures".
Closures capture the variables in their outer function. A top-level function can only use global
variables from their module, function parameters, or local variables:
x = 0 # global
function toplevel(y)
println("x = ", x, " is a global variable")
println("y = ", y, " is a parameter")
z = 2
println("z = ", z, " is a local variable")
end
A closure, on the other hand, can use all those in addition to variables from outer functions that it
captures:
x = 0 # global
function toplevel(y)
println("x = ", x, " is a global variable")
println("y = ", y, " is a parameter")
z = 2
println("z = ", z, " is a local variable")
function closure(v)
println("v = ", v, " is a parameter")
w = 3
println("w = ", w, " is a local variable")
println("x = ", x, " is a global variable")
println("y = ", y, " is a closed variable (a parameter of the outer function)")
println("z = ", z, " is a closed variable (a local of the outer function)")
end
https://riptutorial.com/ 19
end
julia> c = toplevel(10)
x = 0 is a global variable
y = 10 is a parameter
z = 2 is a local variable
(::closure) (generic function with 1 method)
Note that the tail expression of this function is a function in itself; that is, a closure. We can call the
closure c like it was any other function:
julia> c(11)
v = 11 is a parameter
w = 3 is a local variable
x = 0 is a global variable
y = 10 is a closed variable (a parameter of the outer function)
z = 2 is a closed variable (a local of the outer function)
Note that c still has access to the variables y and z from the toplevel call — even though toplevel
has already returned! Each closure, even those returned by the same function, closes over
different variables. We can call toplevel again
julia> d = toplevel(20)
x = 0 is a global variable
y = 20 is a parameter
z = 2 is a local variable
(::closure) (generic function with 1 method)
julia> d(22)
v = 22 is a parameter
w = 3 is a local variable
x = 0 is a global variable
y = 20 is a closed variable (a parameter of the outer function)
z = 2 is a closed variable (a local of the outer function)
julia> c(22)
v = 22 is a parameter
w = 3 is a local variable
x = 0 is a global variable
y = 10 is a closed variable (a parameter of the outer function)
z = 2 is a closed variable (a local of the outer function)
Note that despite d and c having the same code, and being passed the same arguments, their
output is different. They are distinct closures.
https://riptutorial.com/ 20
Chapter 6: Combinators
Remarks
Although combinators have limited practical use, they are a useful tool in education to understand
how programming is fundamentally linked to logic, and how very simple building blocks can
combine to create very complex behaviour. In the context of Julia, learning how to create and use
combinators will strengthen an understanding of how to program in a functional style in Julia.
Examples
The Y or Z Combinator
Although Julia is not a purely functional language, it has full support for many of the cornerstones
of functional programming: first-class functions, lexical scope, and closures.
The fixed-point combinator is a key combinator in functional programming. Because Julia has
eager evaluation semantics (as do many functional languages, including Scheme, which Julia is
heavily inspired by), Curry's original Y-combinator will not work out of the box:
However, a close relative of the Y-combinator, the Z-combinator, will indeed work:
This combinator takes a function and returns a function that when called with argument x, gets
passed itself and x. Why would it be useful for a function to be passed itself? This allows recursion
without actually referencing the name of the function at all!
fact(f, x) = x == 0 ? 1 : x * f(x)
Hence, Z(fact) becomes a recursive implementation of the factorial function, despite no recursion
being visible in this function definition. (Recursion is evident in the definition of the Z combinator, of
course, but that is inevitable in an eager language.) We can verify that our function indeed works:
julia> Z(fact)(10)
3628800
Not only that, but it is as fast as we can expect from a recursive implementation. The LLVM code
demonstrates that the result is compiled into a plain old branch, subtract, call, and multiply:
https://riptutorial.com/ 21
top:
%1 = icmp eq i64 %0, 0
br i1 %1, label %L11, label %L8
The SKI combinator system is sufficient to represent any lambda calculus terms. (In practice, of
course, lambda abstractions blow up to exponential size when they are translated into SKI.) Due
to the simplicity of the system, implementing the S, K, and I combinators is extraordinarily simple:
We can confirm, using the unit testing system, that each combinator has the expected behaviour.
The I combinator is easiest to verify; it should return the given value unchanged:
using Base.Test
@test I(1) === 1
@test I(I) === I
@test I(S) === S
The K combinator is also fairly straightforward: it should discard its second argument.
The S combinator is the most complex; its behaviour can be summarized as applying the first two
arguments to the third argument, the applying the first result to the second. We can most easily
test the S combinator by testing some of its curried forms. S(K), for instance, should simply return
its second argument and discard its first, as we see happens:
https://riptutorial.com/ 22
@test S(I)(I)(I) === I
@test S(I)(I)(K) === K(K)
@test S(I)(I)(S(I)) === S(I)(S(I))
The I combinator described above has a name in standard Base Julia: identity. Thus, we could
have rewritten the above definitions with the following alternative definition of I:
const I = identity
julia> S
(::#3) (generic function with 1 method)
julia> K
(::#9) (generic function with 1 method)
julia> I
(::#13) (generic function with 1 method)
with some more informative displays? The answer is yes! Let's restart the REPL, and this time
define how each function is to be shown:
It's important to avoid showing anything until we have finished defining functions. Otherwise, we
risk invalidating the method cache, and our new methods will not seem to immediately take effect.
This is why we have put semicolons in the above definitions. The semicolons suppress the REPL's
output.
julia> S
S
julia> K
K
https://riptutorial.com/ 23
julia> I
I
julia> S(K)
(::#2) (generic function with 1 method)
It would be nicer to display that as S(K). To do that, we must exploit that the closures have their
own individual types. We can access these types and add methods to them through reflection,
using typeof and the primary field of the name field of the type. Restart the REPL again; we will
make further changes:
julia> S(K)
S(K)
julia> S(K)(I)
S(K)(I)
julia> K
K
julia> K(I)
K(I)
julia> K(I)(K)
I
https://riptutorial.com/ 24
Chapter 7: Comparisons
Syntax
• x < y # if x is strictly less than y
• x > y # if x is strictly greater than y
• x == y # if x is equal to y
• x === y # alternatively x ≡ y, if x is egal to y
• x ≤ y # alternatively x <= y, if x is less than or equal to y
• x ≥ y # alternatively x >= y, if x is greater than or equal to y
• x ≠ y # alternatively x != y, if x is not equal to y
• x ≈ y # if x is approximately equal to y
Remarks
Be careful about flipping comparison signs around. Julia defines many comparison functions by
default without defining the corresponding flipped version. For instance, one can run
Examples
Chained Comparisons
Multiple comparison operators used together are chained, as if connected via the && operator. This
can be useful for readable and mathematically concise comparison chains, such as
However, there is an important difference between a > b > c and a > b && b > c; in the latter, the
term b is evaluated twice. This does not matter much for plain old symbols, but could matter if the
terms themselves have side effects. For instance,
https://riptutorial.com/ 25
julia> 3 > f("test") > 1
test
true
Let’s take a deeper look at chained comparisons, and how they work, by seeing how they are
parsed and lowered into expressions. First, consider the simple comparison, which we can see is
just a plain old function call:
Now if we chain the comparison, we notice that the parsing has changed:
and we note indeed that this is the same as for a > b && b >= c:
https://riptutorial.com/ 26
Ordinal Numbers
We will look at how to implement custom comparisons by implementing a custom type, ordinal
numbers. To simplify the implementation, we will focus on a small subset of these numbers: all
ordinal numbers up to but not including ε₀. Our implementation is focused on simplicity, not speed;
however, the implementation is not slow either.
We store ordinal numbers by their Cantor normal form. Because ordinal arithmetic is not
commutative, we will take the common convention of storing most significant terms first.
Since the Cantor normal form is unique, we may test equality simply through recursive equality:
0.5.0
In version v0.5, there is a very nice syntax for doing this compactly:
import Base: ==
α::OrdinalNumber == β::OrdinalNumber = α.βs == β.βs && α.cs == β.cs
0.5.0
import Base: ==
==(α::OrdinalNumber, β::OrdinalNumber) = α.βs == β.βs && α.cs == β.cs
To finish our order, because this type has a total order, we should overload the isless function:
To test our order, we can create some methods to make ordinal numbers. Zero, of course, is
obtained by having no terms in the Cantor normal form:
https://riptutorial.com/ 27
We can defined an expω to compute ω^α, and use that to compute 1 and ω:
4-element Array{OrdinalNumber,1}:
OrdinalNumber(OrdinalNumber[],Int64[])
OrdinalNumber(OrdinalNumber[OrdinalNumber(OrdinalNumber[],Int64[])],[1])
OrdinalNumber(OrdinalNumber[OrdinalNumber(OrdinalNumber[OrdinalNumber(OrdinalNumber[],Int64[])],[1])],[
OrdinalNumber(OrdinalNumber[OrdinalNumber(OrdinalNumber[OrdinalNumber(OrdinalNumber[OrdinalNumber(Ordin
In the last example, we see that the printing of ordinal numbers could be better, but the result is as
expected.
Standard Operators
1. All of the following unicode sequences: > < >= ≥ <= ≤ == === ≡ != ≠ !== ≢ ∈ ∉ ∋ ∌ ⊆ ⊈ ⊂ ⊄ ⊊ ∝ ∊
∍∥∦∷∺∻∽∾≁≃≄ ≅ ≆≇≈≉≊≋ ≍≎≐≑≒≓≔≕≖≗ ≙ ≚ ≜ ≣≦≧≨≩≪≫≬≭≮≯≰≱≲≳≴≵≶≷≸≹≺
≻≼≽≾≿⊀⊁⊃⊅⊇⊉⊋⊏⊐⊑⊒⊜⊩⊬⊮ ⊲⊳⊴⊵⊶⊷⋍⋐⋑⋕⋖⋗⋘⋙⋚⋛⋜⋝⋞⋟⋠⋡⋢⋣⋤⋥⋦⋧⋨⋩⋪⋫⋬⋭
⊢ ⊣;
2. All symbols in point 1, preceded by a dot (.) to be made elementwise;
3. The operators <:, >:, .!, and in, which cannot be preceded by a dot (.).
Not all of these have a definition in the standard Base library. However, they are available for other
packages to define and use as appropriate.
In everyday use, most of these comparison operators are not relevant. The most common ones
used are the standard mathematical functions for ordering; see the Syntax section for a list.
Like most other operators in Julia, comparison operators are functions and can be called as
functions. For instance, (<)(1, 2) is identical in meaning to 1 < 2.
https://riptutorial.com/ 28
Using ==, ===, and isequal
There are three equality operators: ==, ===, and isequal. (The last is not really an operator, but it is
a function and all operators are functions.)
When to use ==
== is value equality. It returns true when two objects represent, in their present state, the same
value.
julia> 1 == 1
true
but furthermore
julia> 1 == 1.0
true
julia> 1 == 1//1
true
The right hand sides of each equality above are of a different type, but they still represent the
same value.
julia> A = [1, 2, 3]
3-element Array{Int64,1}:
1
2
3
julia> B = [1, 2, 3]
3-element Array{Int64,1}:
1
2
3
julia> C = [1, 3, 2]
3-element Array{Int64,1}:
1
3
2
julia> A == B
true
julia> A == C
false
https://riptutorial.com/ 29
julia> A[2], A[3] = A[3], A[2] # swap 2nd and 3rd elements of A
(3,2)
julia> A
3-element Array{Int64,1}:
1
3
2
julia> A == B
false
julia> A == C
true
julia> 1 === 1
true
because although 1 and 1.0 are the same value, they are of different types, and so the program
can tell them apart.
Furthermore,
julia> A = [1, 2, 3]
3-element Array{Int64,1}:
1
2
3
julia> B = [1, 2, 3]
3-element Array{Int64,1}:
1
2
3
julia> A === B
false
julia> A === A
true
https://riptutorial.com/ 30
which may at first seem surprising! How could the program distinguish between the two vectors A
and B? Because vectors are mutable, it could modify A, and then it would behave differently from B.
But no matter how it modifies A, A will always behave the same as A itself. So A is egal to A, but not
egal to B.
julia> C = A
3-element Array{Int64,1}:
1
2
3
julia> A === C
true
By assigning A to C, we say that C has aliased A. That is, it has become just another name for A.
Any modifications done to A will be observed by C also. Therefore, there is no way to tell the
difference between A and C, so they are egal.
This possibly surprising result is defined by the IEEE standard for floating point types (IEEE-754).
But this is not useful in some cases, such as sorting. isequal is provided for those cases:
On the flip side of the spectrum, == treats IEEE negative zero and positive zero as the same value
(also as specified by IEEE-754). These values have distinct representations in memory, however.
julia> 0.0
0.0
julia> -0.0
-0.0
https://riptutorial.com/ 31
Read Comparisons online: https://riptutorial.com/julia-lang/topic/5563/comparisons
https://riptutorial.com/ 32
Chapter 8: Comprehensions
Examples
Array comprehension
Basic Syntax
Julia's array comprehensions use the following syntax:
Note that as with for loops, all of =, in, and ∈ are accepted for the comprehension.
This is roughly equivalent to creating an empty array and using a for loop to push! items to it.
result = []
for element in iterable
push!(result, expression)
end
however, the type of an array comprehension is as narrow as possible, which is better for
performance.
For example, to get an array of the squares of the integers from 1 to 10, the following code may be
used.
squares = []
for x in 1:10
push!(squares, x^2)
end
Before the Julia 0.5, there is no way to use conditions inside the array comprehensions. But, it is
no longer true. In Julia 0.5 we can use the conditions inside conditions like the following:
https://riptutorial.com/ 33
Source of the above example can be found here.
Nested for loops may be used to iterate over a number of unique iterables.
result = []
for a = iterable_a
for b = iterable_b
push!(result, expression)
end
end
For example, the following may be used to generate the Cartesian product of 1:3 and 1:2.
Flattened multidimensional array comprehensions are similar, except that they lose the shape. For
example,
is a flattened variant of the above. The syntactic difference is that an additional for is used instead
of a comma.
Generator Comprehensions
Generator comprehensions follow a similar format to array comprehensions, but use parentheses
https://riptutorial.com/ 34
() instead of square brackets [].
Function Arguments
Generator comprehensions may be provided as the only argument to a function, without the need
for an extra set of parentheses.
However, if more than one argument is provided, the generator comprehension requries its own
set of parentheses.
https://riptutorial.com/ 35
Chapter 9: Conditionals
Syntax
• if cond; body; end
• if cond; body; else; body; end
• if cond; body; elseif cond; body; else; end
• if cond; body; elseif cond; body; end
• cond ? iftrue : iffalse
• cond && iftrue
• cond || iffalse
• ifelse(cond, iftrue, iffalse)
Remarks
All conditional operators and functions involve using boolean conditions (true or false). In Julia,
the type of booleans is Bool. Unlike some other languages, other kinds of numbers (like 1 or 0),
strings, arrays, and so forth cannot be used directly in conditionals.
Typically, one uses either predicate functions (functions that return a Bool) or comparison
operators in the condition of a conditional operator or function.
Examples
if...else expression
The most common conditional in Julia is the if...else expression. For instance, below we
implement the Euclidean algorithm for computing the greatest common divisor, using a conditional
to handle the base case:
mygcd(a, b) = if a == 0
abs(b)
else
mygcd(b % a, a)
end
The if...else form in Julia is actually an expression, and has a value; the value is the expression in
tail position (that is, the last expression) on the branch that is taken. Consider the following sample
input:
Here, a is 0 and b is -10. The condition a == 0 is true, so the first branch is taken. The returned
value is abs(b) which is 10.
https://riptutorial.com/ 36
julia> mygcd(2, 3)
1
Here, a is 2 and b is 3. The condition a == 0 is false, so the second branch is taken, and we
compute mygcd(b % a, a), which is mygcd(3 % 2, 2). The % operator returns the remainder when 3 is
divided by 2, in this case 1. Thus we compute mygcd(1, 2), and this time a is 1 and b is 2. Once
again, a == 0 is false, so the second branch is taken, and we compute mygcd(b % a, a), which is
mygcd(0, 1). This time, a == 0 at last and so abs(b) is returned, which gives the result 1.
if...else statement
name = readline()
if startswith(name, "A")
println("Your name begins with A.")
else
println("Your name does not begin with A.")
end
Any expression, such as the if...else expression, can be put in statement position. This ignores its
value but still executes the expression for its side effects.
if statement
Like any other expression, the return value of an if...else expression can be ignored (and hence
discarded). This is generally only useful when the body of the expression has side effects, such as
writing to a file, mutating variables, or printing to the screen.
Furthermore, the else branch of an if...else expression is optional. For instance, we can write the
following code to output to screen only if a particular condition is met:
second = Dates.second(now())
if iseven(second)
println("The current second, $second, is even.")
end
In the example above, we use time and date functions to get the current second; for instance, if it
is currently 10:55:27, the variable second will hold 27. If this number is even, then a line will be
printed to screen. Otherwise, nothing will be done.
pushunique!(A, x) = x in A ? A : push!(A, x)
https://riptutorial.com/ 37
In this example, we add x to the collection A only if x is not already in A. Otherwise, we just leave A
unchanged.
• Julia Documentation
• Wikibooks
For branching
The short-circuiting conditional operators && and || can be used as lightweight replacements for
the following constructs:
• x && y is equivalent to x ? y : x
• x || y is equivalent to x ? x : y
One use for short-circuit operators is as a more concise way to test a condition and perform a
certain action depending on that condition. For instance, the following code uses the && operator to
throw an error if the argument x is negative:
function mysqrt(x)
x < 0 && throw(DomainError("x is negative"))
x ^ 0.5
end
The || operator can also be used for error checking, except that it triggers the error unless a
condition holds, instead of if the condition holds:
function halve(x::Integer)
iseven(x) || throw(DomainError("cannot halve an odd number"))
x ÷ 2
end
Another useful application of this is to supply a default value to an object, only if it is not previously
defined:
isdefined(:x) || (x = NEW_VALUE)
Here, this checks if the symbol x is defined (i.e. if there is an value assigned to the object x). If so,
then nothing happens. But, if not, then x will be assigned NEW_VALUE. Note that this example will only
work at toplevel scope.
In conditions
The operators are also useful because they can be used to test two conditions, the second of
which is only evaluated depending on the result of the first condition. From the Julia
https://riptutorial.com/ 38
documentation:
Thus, while both a & b and a && b will yield true if both a and b are true, their behavior if a is false
is different.
For instance, suppose we wish to check if an object is a positive number, where it is possible that
it might not even be a number. Consider the differences between these two attempted
implementations:
CheckPositive1("a")
CheckPositive2("a")
CheckPositive2(), however, will yield false (rather than an error) if a non-numeric type is supplied
to it, since the second expression is only evaluated if the first is true.
d = Dates.dayofweek(now())
if d == 7
println("It is Sunday!")
elseif d == 6
println("It is Saturday!")
elseif d == 5
println("Almost the weekend!")
else
println("Not the weekend yet...")
end
Any number of elseif branches may be used with an if statement, possibly with or without a final
else branch. Subsequent conditions will only be evaluated if all prior conditions have been found to
be false.
https://riptutorial.com/ 39
Usage:
julia> shift(10)
9
julia> shift(11)
12
julia> shift(-1)
-2
The ifelse function will evaluate both branches, even the one that is not selected. This can be
useful either when the branches have side effects that must be evaluated, or because it can be
faster if both branches themselves are cheap.
https://riptutorial.com/ 40
Chapter 10: Cross-Version Compatibility
Syntax
• using Compat
• Compat.String
• Compat.UTF8String
• @compat f.(x, y)
Remarks
It is sometimes very difficult to get new syntax to play well with multiple versions. As Julia is still
undergoing active development, it is often useful simply to drop support for older versions and
instead target just the newer ones.
Examples
Version numbers
Julia has a built-in implementation of semantic versioning exposed through the VersionNumber type.
Alternatively, one can call the VersionNumber constructor; note that the constructor accepts up to
five arguments, but all except the first are optional.
Version numbers can be compared using comparison operators, and thus can be sorted:
Version numbers are used in several places across Julia. For instance, the VERSION constant is a
https://riptutorial.com/ 41
VersionNumber:
julia> VERSION
v"0.5.0"
This is commonly used for conditional code evaluation, depending on the Julia version. For
example, to run different code on v0.4 and v0.5, one can do
julia> Pkg.installed("StatsBase")
v"0.9.0"
Using Compat.jl
The Compat.jl package enables using some new Julia features and syntax with older versions of
Julia. Its features are documented on its README, but a summary of useful applications is given
below.
0.5.0
buf = IOBuffer()
println(buf, "Hello World!")
String(buf) # "Hello World!\n"
can be directly translated to this code, which works on both v0.5 and v0.4:
using Compat
buf = IOBuffer()
println(buf, "Hello World!")
Compat.String(buf) # "Hello World!\n"
https://riptutorial.com/ 42
advised, as it will mean String on v0.5, and UTF8String on v0.4, both of which are concrete
types.
• One has to be careful to use Compat.String or import Compat: String, because String itself
has a meaning on v0.4: it is a deprecated alias for AbstractString. A sign that String was
accidentally used instead of Compat.String is if at any point, the following warnings appear:
f.(x, y)
is lowered to broadcast(f, x, y). Examples of using this syntax include sin.([1, 2, 3]) to take the
sine of multiple numbers at once.
Luckily, Compat makes this new syntax usable from v0.4 also. Once again, we add using Compat.
This time, we surround the expression with the @compat macro:
https://riptutorial.com/ 43
Chapter 11: Dictionaries
Examples
Using Dictionaries
You can get entries in a dictionary putting the key in square brackets.
julia> dict["A"]
1
https://riptutorial.com/ 44
Chapter 12: Enums
Syntax
• @enum EnumType val=1 val val
• :symbol
Remarks
It is sometimes useful to have enumerated types where each instance is of a different type (often a
singleton immutable type); this can be important for type stability. Traits are typically implemented
with this paradigm. However, this results in additional compile-time overhead.
Examples
Defining an enumerated type
An enumerated type is a type that can hold one of a finite list of possible values. In Julia,
enumerated types are typically called "enum types". For instance, one could use enum types to
describe the seven days of the week, the twelve months of the year, the four suits of a standard
52-card deck, or other similar situations.
We can define enumerated types to model the suits and ranks of a standard 52-card deck. The
@enum macro is used to define enum types.
@enum Suit ♣ ♦ ♥ ♠
@enum Rank ace=1 two three four five six seven eight nine ten jack queen king
This defines two types: Suit and Rank. We can check that the values are indeed of the expected
types:
julia> ♦
♦::Suit = 1
julia> six
six::Rank = 6
Note that each suit and rank has been associated with a number. By default, this number starts at
zero. So the second suit, diamonds, was assigned the number 1. In the case of Rank, it may make
more sense to start the number at one. This was achieved by annotating the definition of ace with
a =1 annotation.
Enumerated types come with a lot of functionality, such as equality (and indeed identity) and
comparisons built in:
https://riptutorial.com/ 45
julia> seven === seven
true
Like values of any other immutable type, values of enumerated types can also be hashed and
stored in Dicts.
We can complete this example by defining a Card type that has a Rank and a Suit field:
immutable Card
rank::Rank
suit::Suit
end
julia> Card(three, ♣)
Card(three::Rank = 3,♣::Suit = 0)
But enumerated types also come with their own convert methods, so we can indeed simply do
julia> Card(7, ♠)
Card(seven::Rank = 7,♠::Suit = 3)
and since 7 can be directly converted to Rank, this constructor works out of the box.
We might wish to define syntactic sugar for constructing these cards; implicit multiplication
provides a convenient way to do it. Define
and then
julia> 10♣
Card(ten::Rank = 10,♣::Suit = 0)
julia> 5♠
Card(five::Rank = 5,♠::Suit = 3)
https://riptutorial.com/ 46
Although the @enum macro is quite useful for most use cases, it can be excessive in some use
cases. Disadvantages of @enum include:
In cases where a lighter-weight alternative is desired, the Symbol type can be used. Symbols are
interned strings; they represent sequences of characters, much like strings do, but they are
uniquely associated with numbers. This unique association enables fast symbol equality
comparison.
We may again implement a Card type, this time using Symbol fields:
const ranks = Set([:ace, :two, :three, :four, :five, :six, :seven, :eight, :nine,
:ten, :jack, :queen, :king])
const suits = Set([:♣, :♦, :♥, :♠])
immutable Card
rank::Symbol
suit::Symbol
function Card(r::Symbol, s::Symbol)
r in ranks || throw(ArgumentError("invalid rank: $r"))
s in suits || throw(ArgumentError("invalid suit: $s"))
new(r, s)
end
end
We implement the inner constructor to check for any incorrect values passed to the constructor.
Unlike in the example using @enum types, Symbols can contain any string, and so we must be careful
about what kinds of Symbols we accept. Note here the use of the short-circuit conditional operators.
A major benefit of Symbols is their runtime extensibility. If at runtime, we wish to accept (for
example) :eleven as a new rank, it suffices to simply run push!(ranks, :eleven). Such runtime
extensibility is not possible with @enum types.
https://riptutorial.com/ 47
Chapter 13: Expressions
Examples
Intro to Expressions
Expressions are a specific type of object in Julia. You can think of an expression as representing a
piece of Julia code that has not yet been evaluated (i.e. executed). There are then specific
functions and operations, like eval() which will evaluate the expression.
For instance, we could write a script or enter into the interpreter the following: julia> 1+1 2
One way to create an expression is using the :() syntax. For example:
We now have an Expr type object. Having just been formed, it doesn't do anything - it just sits
around like any other object until it is acted upon. In this case, we can evaluate that expression
using the eval() function:
julia> eval(MyExpression)
2
1+1
eval(:(1+1))
Why would we want to go through the much more complicated syntax in eval(:(1+1)) if we just
want to find what 1+1 equals? The basic reason is that we can define an expression at one point
in our code, potentially modify it later on, and then evaluate it at a later point still. This can
potentially open up powerful new capabilities to the Julia programmer. Expressions are a key
component of metaprogramming in Julia.
Creating Expressions
There are a number of different methods that can be used to create the same type of expression.
The expressions intro mentioned the :() syntax. Perhaps the best place to start, however is with
strings. This helps to reveal some of the fundamental similarities between expressions and strings
in Julia.
https://riptutorial.com/ 48
Every Julia program starts life as a string
In other words, any Julia script is simply written in a text file, which is nothing but a string of
characters. Likewise, any Julia command entered into an interpreter is just a string of characters.
The role of Julia or any other programming language then is to interpret and evaluate strings of
characters in a logical, predictable way so that those strings of characters can be used to describe
what the programmer wants the computer to accomplish.
Thus, one way to create an expression is to use the parse() function as applied to a string. The
following expression, once it is evaluated, will assign the value of 2 to the symbol x.
MyExpr2 = :(x = 2)
julia> MyExpr == MyExpr2
true
Note that with this syntax, Julia will automatically treat the names of objects as referring to
symbols. We can see this if we look at the args of the expression. (See Fields of Expression
Objects for more details on the args field in an expression.)
julia> MyExpr2.args
2-element Array{Any,1}:
:x
2
This syntax is based on prefix notation. In other words, the first argument of the specified to the
Expr() function is the head or prefix. The remaining are the arguments of the expression. The head
determines what operations will be performed on the arguments.
When using this syntax, it is important to distinguish between using objects and symbols for
objects. For instance, in the above example, the expression assigns the value of 2 to the symbol
:x, a perfectly sensible operation. If we used x itself in an expression such as that, we would get
the nonsensical result:
https://riptutorial.com/ 49
julia> Expr(:(=), x, 5)
:(2 = 5)
Thus, the Expr() function does not perform the same automatic transformation into symbols as the
:() syntax for creating expressions.
MyQuote =
quote
x = 2
y = 3
end
julia> typeof(MyQuote)
Expr
Note that with quote...end we can create expressions that contain other expressions in their args
field:
julia> typeof(MyQuote.args[2])
Expr
This Example just gives the basics for creating expressions. See also, for example, Interpolation
and Expressions and Fields of Expression Objects for more information on creating more complex
and advanced expressions.
As mentioned in the Intro to Expressions expressions are a specific type of object in Julia. As
such, they have fields. The two most used fields of an expression are its head and its args. For
instance, consider the expression
discussed in Creating Expressions. We can see the head and args as follows:
julia> MyExpr3.head
:(=)
https://riptutorial.com/ 50
julia> MyExpr3.args
2-element Array{Any,1}:
:x
2
Expressions are based on prefix notation. As such, the head generally specifies the operation that
is to be performed on the args. The head must be of Julia type Symbol.
When an expression is to assign a value (when it gets evaluated), it will generally use a head of
:(=). There are of course obvious variations to this that can be employed, e.g.:
Following the conventions of prefix notation, operators are evaluated from left to right. Thus, this
expression here means that we will call the function that is specified on the first element of args on
the subsequent elements. We similarly could have:
julia> eval(ex2b)
2x2 Array{Float64,2}:
0.429397 0.164478
0.104994 0.675745
Note that :call is implicitly used as the head in certain constructions of expressions, e.g.
Thus, with the :() syntax for creating expressions, Julia will seek to automatically determine the
correct head to use. Similarly:
https://riptutorial.com/ 51
In fact, if you aren't certain what the right head to use for an expression that you are forming using,
for instance, Expr() this can be a helpful tool to get tips and ideas for what to use.
Creating Expressions mentions that expressions are closely related to strings. As such, the
principles of interpolation within strings are also relevant for Expressions. For instance, in basic
string interpolation, we can have something like:
n = 2
julia> MyString = "there are $n ducks"
"there are 2 ducks"
We use the $ sign to insert the value of n into the string. We can use the same technique with
expressions. E.g.
a = 2
ex1 = :(x = 2*$a) ## :(x = 2 * 2)
a = 3
eval(ex1)
x # 4
a = 2
ex2 = :(x = 2*a) # :(x = 2a)
a = 3
eval(ex2)
x # 6
Thus, with the first example, we set in advance the value of a that will be used at the time that the
expression is evaluated. With the second example, however, the Julia compiler will only look to a
to find its value at the time of evaluation for our expression.
There are a number of useful web resources that can help further your knowledge of expressions
in Julia. These include:
SO Posts:
https://riptutorial.com/ 52
Read Expressions online: https://riptutorial.com/julia-lang/topic/5805/expressions
https://riptutorial.com/ 53
Chapter 14: for Loops
Syntax
• for i in iter; ...; end
• while cond; ...; end
• break
• continue
• @parallel (op) for i in iter; ...; end
• @parallel for i in iter; ...; end
• @goto label
• @label label
Remarks
Whenever it makes code shorter and easier to read, consider using higher-order functions, such
as map or filter, instead of loops.
Examples
Fizz Buzz
A common use case for a for loop is to iterate over a predefined range or collection, and do the
same task for all its elements. For instance, here we combine a for loop with a conditional if-
elseif-else statement:
for i in 1:100
if i % 15 == 0
println("FizzBuzz")
elseif i % 3 == 0
println("Fizz")
elseif i % 5 == 0
println("Buzz")
else
println(i)
end
end
This is the classic Fizz Buzz interview question. The (truncated) output is:
1
2
Fizz
4
Buzz
Fizz
7
8
https://riptutorial.com/ 54
Find smallest prime factor
In some situations, one might want to return from a function before finishing an entire loop. The
return statement can be used for this.
function primefactor(n)
for i in 2:n
if n % i == 0
return i
end
end
@assert false # unreachable
end
Usage:
julia> primefactor(100)
2
julia> primefactor(97)
97
Loops can also be terminated early with the break statement, which terminates just the enclosing
loop instead of the entire function.
Multidimensional iteration
In Julia, a for loop can contain a comma (,) to specify iterating over multiple dimensions. This acts
similarly to nesting a loop within another, but can be more compact. For instance, the below
function generates elements of the Cartesian product of two iterables:
Usage:
However, indexing over arrays of any dimension should be done with eachindex, not with a
multidimensional loop (if possible):
https://riptutorial.com/ 55
s = zero(eltype(A))
for ind in eachindex(A)
s += A[ind]
end
Julia provides macros to simplify distributing computation across multiple machines or workers.
For instance, the following computes the sum of some number of squares, possibly in parallel.
function sumofsquares(A)
@parallel (+) for i in A
i ^ 2
end
end
Usage:
julia> sumofsquares(1:10)
385
For more on this topic, see the example on @parallel within the Parallel Processesing topic.
https://riptutorial.com/ 56
Chapter 15: Functions
Syntax
• f(n) = ...
• function f(n) ... end
• n::Type
• x -> ...
• f(n) do ... end
Remarks
Aside from generic functions (which are most common), there are also built-in functions. Such
functions include is, isa, typeof, throw, and similar functions. Built-in functions are typically
implemented in C instead of Julia, so they cannot be specialized on argument types for dispatch.
Examples
Square a number
square(n) = n * n
julia> square(10)
100
Functions are objects in Julia, and we can show them in the REPL as with any other objects:
julia> square
square (generic function with 1 method)
All Julia functions are generic (otherwise known as polymorphic) by default. Our square function
works just as well with floating point values:
julia> square(2.5)
6.25
julia> square([2 4
2 1])
2×2 Array{Int64,2}:
https://riptutorial.com/ 57
12 12
6 9
Recursive functions
Simple recursion
Using recursion and the ternary conditional operator, we can create an alternative implementation
of the built-in factorial function:
myfactorial(n) = n == 0 ? 1 : n * myfactorial(n - 1)
Usage:
julia> myfactorial(10)
3628800
This function is compact and uses a variety of more advanced techniques, such as the reduce
higher order function, the Set data type, and generator expressions.
Introduction to Dispatch
Usage:
julia> describe(10)
"integer 10"
julia> describe(1.0)
https://riptutorial.com/ 58
"floating point 1.0"
Unlike many languages, which typically provide either static multiple dispatch or dynamic single
dispatch, Julia has full dynamic multiple dispatch. That is, functions can be specialized for more
than one argument. This comes in handy when defining specialized methods for operations on
certain types, and fallback methods for other types.
Usage:
Optional Arguments
Julia allows functions to take optional arguments. Behind the scenes, this is implemented as
another special case of multiple dispatch. For instance, let's solve the popular Fizz Buzz problem.
By default, we will do it for numbers in the range 1:10, but we will allow a different value if
necessary. We will also allow different phrases to be used for Fizz or Buzz.
If we inspect fizzbuzz in the REPL, it says that there are four methods. One method was created
for each combination of arguments allowed.
julia> fizzbuzz
fizzbuzz (generic function with 4 methods)
julia> methods(fizzbuzz)
# 4 methods for generic function "fizzbuzz":
fizzbuzz() at REPL[96]:2
https://riptutorial.com/ 59
fizzbuzz(xs) at REPL[96]:2
fizzbuzz(xs, fizz) at REPL[96]:2
fizzbuzz(xs, fizz, buzz) at REPL[96]:2
We can verify that our default values are used when no parameters are provided:
julia> fizzbuzz()
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
but that the optional parameters are accepted and respected if we provide them:
Parametric Dispatch
It is frequently the case that a function should dispatch on parametric types, such as Vector{T} or
Dict{K,V}, but the type parameters are not fixed. This case can be dealt with by using parametric
dispatch:
https://riptutorial.com/ 60
"x"
"y"
"z"
One may be tempted to simply write xs::Vector{Number}. But this only works for objects whose type
is explicitly Vector{Number}:
This is due to parametric invariance: the object Int[1, 2] is not a Vector{Number}, because it can
only contain Ints, whereas a Vector{Number} would be expected to be able to contain any kinds of
numbers.
For example, here is code to compute the sum of squares of a vector of integers:
function sumsq(v::Vector{Int})
s = 0
for x in v
s += x ^ 2
end
s
end
But this code only works for a vector of Ints. It will not work on a UnitRange:
julia> sumsq(1:10)
ERROR: MethodError: no method matching sumsq(::UnitRange{Int64})
Closest candidates are:
sumsq(::Array{Int64,1}) at REPL[8]:2
function sumsq(v::AbstractVector)
s = zero(eltype(v))
https://riptutorial.com/ 61
for x in v
s += x ^ 2
end
s
end
This will work on the two cases listed above. But there are some collections that we might want to
sum the squares of that aren't vectors at all, in any sense. For instance,
function sumsq(v)
s = zero(eltype(v))
for x in v
s += x ^ 2
end
s
end
This is the most idiomatic Julia code, and can handle all sorts of situations. In some other
languages, removing type annotations may affect performance, but that is not the case in Julia;
only type stability is important for performance.
Imperative factorial
A long-form syntax is available for defining multi-line functions. This can be useful when we use
imperative structures such as loops. The expression in tail position is returned. For instance, the
below function uses a for loop to compute the factorial of some integer n:
function myfactorial(n)
fact = one(n)
for m in 1:n
fact *= m
end
fact
end
Usage:
https://riptutorial.com/ 62
julia> myfactorial(10)
3628800
In longer functions, it is common to see the return statement used. The return statement is not
necessary in tail position, but it is still sometimes used for clarity. For instance, another way of
writing the above function would be
function myfactorial(n)
fact = one(n)
for m in 1:n
fact *= m
end
return fact
end
Anonymous functions
Arrow syntax
Anonymous functions can be created using the -> syntax. This is useful for passing functions to
higher-order functions, such as the map function. The below function computes the square of each
number in an array A.
julia> squareall(1:10)
10-element Array{Int64,1}:
1
4
9
16
25
36
49
64
81
100
Multiline syntax
Multiline anonymous functions can be created using function syntax. For instance, the following
example computes the factorials of the first n numbers, but using an anonymous function instead
of the built in factorial.
https://riptutorial.com/ 63
for i in 1:n
product *= i
end
product
end, 1:10)
10-element Array{Int64,1}:
1
2
6
24
120
720
5040
40320
362880
3628800
Do block syntax
Because it is so common to pass an anonymous function as the first argument to a function, there
is a do block syntax. The syntax
map(A) do x
x ^ 2
end
is equivalent to
map(x -> x ^ 2, A)
but the former can be more clear in many situations, especially if a lot of computation is being
done in the anonymous function. do block syntax is especially useful for file input and output for
resource management reasons.
https://riptutorial.com/ 64
Chapter 16: Higher-Order Functions
Syntax
• foreach(f, xs)
• map(f, xs)
• filter(f, xs)
• reduce(f, v0, xs)
• foldl(f, v0, xs)
• foldr(f, v0, xs)
Remarks
Functions can be accepted as parameters and can also be produced as return types. Indeed,
functions can be created inside the body of other functions. These inner functions are known as
closures.
Examples
Functions as arguments
Functions are objects in Julia. Like any other objects, they can be passed as arguments to other
functions. Functions that accept functions are known as higher-order functions.
For instance, we can implement an equivalent of the standard library's foreach function by taking a
function f as the first parameter.
By taking a function as the first parameter, instead of a later parameter, we can use Julia's do
block syntax. The do block syntax is just a convenient way to pass an anonymous function as the
first argument to a function.
https://riptutorial.com/ 65
end
1
4
27
Our implementation of myforeach above is roughly equivalent to the built-in foreach function. Many
other built-in higher order functions also exist.
Higher-order functions are quite powerful. Sometimes, when working with higher-order functions,
the exact operations being performed become unimportant and programs can become quite
abstract. Combinators are examples of systems of highly abstract higher-order functions.
Two of the most fundamental higher-order functions included in the standard library are map and
filter. These functions are generic and can operate on any iterable. In particular, they are well-
suited for computations on arrays.
Suppose we have a dataset of schools. Each school teaches a particular subject, has a number of
classes, and an average number of students per class. We can model a school with the following
immutable type:
immutable School
subject::Symbol
nclasses::Int
nstudents::Int # average no. of students per class
end
Suppose we wish to find the number of students in total enrolled in a math program. To do this, we
require several steps:
• we must narrow the dataset down to only schools that teach math (filter)
• we must compute the number of students at each school (map)
• and we must reduce that list of numbers of students to a single value, the sum (reduce)
A naïve (not most performant) solution would simply be to use those three higher-order functions
directly.
function nmath(data)
maths = filter(x -> x.subject === :math, data)
students = map(x -> x.nclasses * x.nstudents, maths)
reduce(+, 0, students)
end
https://riptutorial.com/ 66
julia> nmath(dataset)
190
Functions exist to combine these functions and thus improve performance. For instance, we could
have used the mapreduce function to perform the mapping and reduction in one step, which would
save time and memory.
The reduce is only meaningful for associative operations like +, but occasionally it is useful to
perform a reduction with a non-associative operation. The higher-order functions foldl and foldr
are provided to force a particular reduction order.
https://riptutorial.com/ 67
Chapter 17: Input
Syntax
• readline()
• readlines()
• readstring(STDIN)
• chomp(str)
• open(f, file)
• eachline(io)
• readstring(file)
• read(file)
• readcsv(file)
• readdlm(file)
Parameters
Parameter Details
The string to strip a trailing newline from. Note that strings are immutable by
str
convention. This function returns a new string.
open(f,
Open a file, call the function, and close the file afterward.
file)
Examples
Reading a String from Standard Input
The STDIN stream in Julia refers to standard input. This can represent either user input, for
interactive command-line programs, or input from a file or pipeline that has been redirected into
the program.
The readline function, when not provided any arguments, will read data from STDIN until a newline
is encountered, or the STDIN stream enters the end-of-file state. These two cases can be
distinguished by whether the \n character has been read as the final character:
julia> readline()
some stuff
https://riptutorial.com/ 68
"some stuff\n"
Often, for interactive programs, we do not care about the EOF state, and just want a string. For
instance, we may prompt the user for input:
function askname()
print("Enter your name: ")
readline()
end
julia> askname()
Enter your name: Julia
"Julia\n"
The chomp function is available to remove up to one trailing newline off a string. For example:
We may therefore augment our function with chomp so that the result is as expected:
function askname()
print("Enter your name: ")
chomp(readline())
end
julia> askname()
Enter your name: Julia
"Julia"
Sometimes, we may wish to read as many lines as is possible (until the input stream enters the
end-of-file state). The readlines function provides that capability.
https://riptutorial.com/ 69
"Q, R, S\n"
"T, U, V\n"
"W, X\n"
"Y, Z\n"
0.5.0
Once again, if we dislike the newlines at the end of lines read by readlines, we can use the chomp
function to remove them. This time, we broadcast the chomp function across the entire array:
julia> chomp.(readlines())
A, B, C, D, E, F, G
H, I, J, K, LMNO, P
Q, R, S
T, U, V
W, X
Y, Z
6-element Array{String,1}:
"A, B, C, D, E, F, G"
"H, I, J, K, LMNO, P"
"Q, R, S"
"T, U, V"
"W, X "
"Y, Z"
Other times, we may not care about lines at all, and simply want to read as much as possible as a
single string. The readstring function accomplishes this:
julia> readstring(STDIN)
If music be the food of love, play on,
Give me excess of it; that surfeiting,
The appetite may sicken, and so die. # [END OF INPUT]
"If music be the food of love, play on,\nGive me excess of it; that surfeiting,\nThe appetite
may sicken, and so die.\n"
(the # [END OF INPUT] is not part of the original input; it has been added for clarity.)
Reading numbers from standard input is a combination of reading strings and parsing such strings
as numbers.
The parse function is used to parse a string into the desired number type:
The format expected by parse(T, x) is similar to, but not exactly the same, as the format Julia
https://riptutorial.com/ 70
expects from number literals:
julia> -00000023
-23
julia> 1_000_000
1000000
Combining the parse and readline functions allows us to read a single number from a line:
function asknumber()
print("Enter a number: ")
parse(Float64, readline())
end
julia> asknumber()
Enter a number: 78.3
78.3
The usual caveats about floating-point precision apply. Note that parse can be used with BigInt
and BigFloat to remove or minimize loss of precision.
Sometimes, it is useful to read more than one number from the same line. Typically, the line can
be split with whitespace:
function askints()
print("Enter some integers, separated by spaces: ")
[parse(Int, x) for x in split(readline())]
end
julia> askints()
Enter some integers, separated by spaces: 1 2 3 4
4-element Array{Int64,1}:
1
2
https://riptutorial.com/ 71
3
4
open("myfile") do f
for (i, line) in enumerate(eachline(f))
print("Line $i: $line")
end
end
Note that eachline is a lazy iterable over the lines of the file. It is preferred to readlines for
performance reasons.
Because do block syntax is just syntactic sugar for anonymous functions, we can pass named
functions to open too:
https://riptutorial.com/ 72
0x65
0x65
0x74
0x2e
0x0a
The functions read and readstring provide convenience methods that will open a file automatically:
julia> readstring("myfile")
"What's in a name? That which we call a rose\nBy any other name would smell as sweet.\n"
Make,Model,Price
Foo,2015A,8000
Foo,2015B,14000
Foo,2016A,10000
Foo,2016B,16000
Bar,2016Q,20000
Then we may use the readcsv function to read this data into a Matrix:
julia> readcsv("file.csv")
6×3 Array{Any,2}:
"Make" "Model" "Price"
"Foo" "2015A" 8000
"Foo" "2015B" 14000
"Foo" "2016A" 10000
"Foo" "2016B" 16000
"Bar" "2016Q" 20000
If the file were instead delimited with tabs, in a file named file.tsv, then the readdlm function can
be used instead, with the delim argument set to '\t'. More advanced workloads should use the
CSV.jl package.
https://riptutorial.com/ 73
Chapter 18: Iterables
Syntax
• start(itr)
• next(itr, s)
• done(itr, s)
• take(itr, n)
• drop(itr, n)
• cycle(itr)
• Base.product(xs, ys)
Parameters
Parameter Details
For Base.product
(Note that product accepts any number of arguments; if more than two are
...
provided, it will construct tuples of length greater than two.)
Examples
New iterable type
In Julia, when looping through an iterable object I is done with the for syntax:
https://riptutorial.com/ 74
end
state = start(I)
while !done(I, state)
(i, state) = next(I, state)
# body
end
Therefore, if you want I to be an iterable, you need to define start, next and done methods for its
type. Suppose you define a type Foo containing an array as one of the fields:
type Foo
bar::Array{Int,1}
end
julia> I = Foo([1,2,3])
Foo([1,2,3])
julia> I.bar
3-element Array{Int64,1}:
1
2
3
If we want to iterate through Foo, with each element bar being returned by each iteration, we define
the methods:
start(I::Foo) = 1
Note that since these functions belong to the Base module, we must first import their names before
adding new methods to them.
After the methods are defined, Foo is compatible with the iterator interface:
julia> for i in I
println(i)
end
https://riptutorial.com/ 75
1
2
3
The standard library comes with a rich collection of lazy iterables (and libraries such as Iterators.jl
provide even more). Lazy iterables can be composed to create more powerful iterables in constant
time. The most important lazy iterables are take and drop, from which many other functions can be
created.
A[10:15]
However, slice notation does not work with all iterables. For instance, we cannot slice a generator
expression:
julia> "αααα"[2:3]
ERROR: UnicodeError: invalid character index
in getindex(::String, ::UnitRange{Int64}) at ./strings/string.jl:130
julia> "αααα"[3:4]
"α"
The implementation here works because for UnitRange value a:b, the following steps are
performed:
In total, b-a elements are taken. We can confirm our implementation is correct in each case above:
https://riptutorial.com/ 76
'α'
'α'
julia> circshift(1:10, 3)
10-element Array{Int64,1}:
8
9
10
1
2
3
4
5
6
7
Can we do this lazily for all iterables? We can use the cycle, drop, and take iterables to implement
this functionality.
Along with lazy types being more performant in many situations, this lets us do circshift-like
functionality on types that would otherwise not support it:
0.5.0
https://riptutorial.com/ 77
• Base.product, which computes a Cartesian product.
• prod, which computes a regular product (as in multiplication)
• :, which creates a range
• map, which is a higher order function applying a function to each element of a collection
Lazily-Evaluated Lists
It's possible to make a simple lazily-evaluated list using mutable types and closures. A lazily-
evaluated list is a list whose elements are not evaluated when it's constructed, but rather when it is
accessed. Benefits of lazily evaluated lists include the possibility of being infinite.
import Base: first, tail, start, next, done, iteratorsize, HasLength, SizeUnknown
abstract List
immutable Cons <: List
head
tail::Lazy
end
immutable Nil <: List end
macro cons(x, y)
quote
Cons($(esc(x)), Lazy(() -> $(esc(y))))
end
end
first(xs::Cons) = xs.head
tail(xs::Cons) = xs.tail[]
start(xs::Cons) = xs
next(::Cons, xs) = first(xs), tail(xs)
done(::List, ::Cons) = false
done(::List, ::Nil) = true
https://riptutorial.com/ 78
iteratorsize(::Nil) = HasLength()
iteratorsize(::Cons) = SizeUnknown()
Which indeed works as it would in a language like Haskell, where all lists are lazily-evaluated:
In practice, it is better to use the Lazy.jl package. However, the implementation of the lazy list
above sheds lights into important details about how to construct one's own iterable type.
https://riptutorial.com/ 79
Chapter 19: JSON
Syntax
• using JSON
• JSON.parse(str)
• JSON.json(obj)
• JSON.print(io, obj, indent)
Remarks
Since neither Julia Dict nor JSON objects are inherently ordered, it's best not to rely on the order
of key-value pairs in a JSON object.
Examples
Installing JSON.jl
JSON is a popular data interchange format. The most popular JSON library for Julia is JSON.jl. To
install this package, use the package manager:
julia> Pkg.add("JSON")
The next step is to test whether the package is working on your machine:
julia> Pkg.test("JSON")
Parsing JSON
JSON that has been encoded as a string can easily be parsed into a standard Julia type:
julia> JSON.parse("""{
"this": ["is", "json"],
"numbers": [85, 16, 12.0],
"and": [true, false, null]
}""")
Dict{String,Any} with 3 entries:
"this" => Any["is","json"]
"numbers" => Any[85,16,12.0]
"and" => Any[true,false,nothing]
https://riptutorial.com/ 80
• JSON types map to sensible types in Julia: Object becomes Dict, array becomes Vector,
number becomes Int64 or Float64, boolean becomes Bool, and null becomes nothing::Void.
• JSON is an untyped container format: Thus returned Julia vectors are of type Vector{Any},
and returned dictionaries are of type Dict{String, Any}.
• JSON standard does not distinguish between integers and decimal numbers, but JSON.jl
does. A number without a decimal point or scientific notation is parsed into Int64, whereas a
number with a decimal point is parsed into Float64. This matches closely with the behavior of
JSON parsers in many other languages.
Serializing JSON
The JSON.json function serializes a Julia object into a Julia String containing JSON:
julia> println(ans)
{"c":[1.0,2.0,3.0],"a":"b","d":null}
Note that STDOUT is the default, and can be omitted in the above call.
https://riptutorial.com/ 81
Chapter 20: Metaprogramming
Syntax
• macro name(ex) ... end
• quote ... end
• :(...)
• $x
• Meta.quot(x)
• QuoteNode(x)
• esc(x)
Remarks
Julia’s metaprogramming features are heavily inspired by those of Lisp-like languages, and will
seem familiar to those with some Lisp background. Metaprogramming is very powerful. When
used correctly, it can lead to more concise and readable code.
The quote ... end is quasiquote syntax. Instead of the expressions within being evaluated, they
are simply parsed. The value of the quote ... end expression is the resulting Abstract Syntax Tree
(AST).
The :(...) syntax is similar to the quote ... end syntax, but it is more lightweight. This syntax is
more concise than quote ... end.
Inside a quasiquote, the $ operator is special and interpolates its argument into the AST. The
argument is expected to be an expression which is spliced directly into the AST.
The Meta.quot(x) function quotes its argument. This is often useful in combination with using $ for
interpolation, as it allows expressions and symbols to be spliced literally into the AST.
Examples
Reimplementing the @show macro
In Julia, the @show macro is often useful for debugging purposes. It displays both the expression to
be evaluated and its result, finally returning the value of the result:
julia> @show 1 + 1
1 + 1 = 2
2
https://riptutorial.com/ 82
quote
value = $expression
println($(Meta.quot(expression)), " = ", value)
value
end
end
julia> x = @myshow 1 + 1
1 + 1 = 2
2
julia> x
2
Until loop
We're all used to the while syntax, that executes its body while the condition is evaluated to true.
What if we want to implement an until loop, that executes a loop until the condition is evaluated to
true?
In Julia, we can do this by creating a @until macro, that stops to execute its body when the
condition is met:
Here we have used the function chaining syntax |>, which is equivalent to calling the esc function
on the entire quote block. The esc function prevents macro hygiene from applying to the contents of
the macro; without it, variables scoped in the macro will be renamed to prevent collisions with
outside variables. See the Julia documentation on macro hygiene for more details.
You can use more than one expression in this loop, by simply putting everything inside a begin ...
end block:
julia> i = 0;
https://riptutorial.com/ 83
6
7
8
9
julia> i
10
julia> QuoteNode(:x)
:(:x)
julia> Meta.quot(:x)
:(:x)
What does "quoting" mean, and what is it good for? Quoting allows us to protect expressions from
being interpreted as special forms by Julia. A common use case is when we generate expressions
that should contain things that evaluate to symbols. (For example, this macro needs to return a
expression that evaluates to a symbol.) It doesn't work simply to return the symbol:
julia> @mysym
ERROR: UndefVarError: x not defined
julia> macroexpand(:(@mysym))
:x
What's going on here? @mysym expands to :x, which as an expression becomes interpreted as the
variable x. But nothing has been assigned to x yet, so we get an x not defined error.
julia> @mysym2
:x
julia> macroexpand(:(@mysym2))
:(:x)
Here, we have used the Meta.quot function to turn our symbol into a quoted symbol, which is the
result we want.
What is the difference between Meta.quot and QuoteNode, and which should I use? In almost all
https://riptutorial.com/ 84
cases, the difference does not really matter. It is perhaps a little safer sometimes to use QuoteNode
instead of Meta.quot. Exploring the difference is informative into how Julia expressions and macros
work, however.
In short, the difference is that Meta.quot allows interpolation within the quoted thing, while QuoteNode
protects its argument from any interpolation. To understand interpolation, it is important to mention
the $ expression. There is a kind of expression in Julia called a $ expression. These expressions
allow for escaping. For instance, consider the following expression:
When evaluated, this expression will evaluate 1 and assign it to x, then construct an expression of
the form _ + _ where the _ will be replaced by the value of x. Thus, the result of this should be the
expression 1 + 1 (which is not yet evaluated, and so distinct from the value 2). Indeed, this is the
case:
julia> eval(ex)
:(1 + 1)
Let's say now that we're writing a macro to build these kinds of expressions. Our macro will take
an argument, which will replace the 1 in the ex above. This argument can be any expression, of
course. Here is something that is not quite what we want:
julia> @makeex 1
quote
x = $(Expr(:escape, 1))
$(Expr(:quote, :($(Expr(:$, :x)) + $(Expr(:$, :x)))))
end
julia> @makeex 1 + 1
quote
x = $(Expr(:escape, 2))
$(Expr(:quote, :($(Expr(:$, :x)) + $(Expr(:$, :x)))))
end
https://riptutorial.com/ 85
The second case is incorrect, because we ought to keep 1 + 1 unevaluated. We fix that by quoting
the argument with Meta.quot:
julia> @makeex2 1 + 1
quote
x = 1 + 1
$(Expr(:quote, :($(Expr(:$, :x)) + $(Expr(:$, :x)))))
end
Macro hygiene does not apply to the contents of a quote, so escaping is not necessary in this case
(and in fact not legal) in this case.
From the first example, we see that interpolation allows us to inline the sin(1), instead of having
the expression be a literal sin(1). The second example shows that this interpolation is done in the
macro invocation scope, not the macro's own scope. That's because our macro hasn't actually
evaluated any code; all it's doing is generating code. The evaluation of the code (which makes its
way into the expression) is done when the expression the macro generates is actually run.
What if we had used QuoteNode instead? As you may guess, since QuoteNode prevents interpolation
from happening at all, this means it won't work.
https://riptutorial.com/ 86
end
In this example, we might agree that Meta.quot gives greater flexibility, as it allows interpolation. So
why might we ever consider using QuoteNode? In some cases, we may not actually desire
interpolation, and actually want the literal $ expression. When would that be desirable? Let's
consider a generalization of @makeex where we can pass additional arguments determining what
comes to the left and right of the + sign:
julia> eval(ans)
:(1 + 1)
A limitation of our implementation of @makeex4 is that we can't use expressions as either the left and
right sides of the expression directly, because they get interpolated. In other words, the
expressions may get evaluated for interpolation, but we might want them preserved. (Since there
are many levels of quoting and evaluation here, let us clarify: our macro generates code that
constructs an expression that when evaluated produces another expression. Phew!)
julia> eval(ans)
:(0.5 + 1)
https://riptutorial.com/ 87
We ought to allow the user to specify when interpolation is to happen, and when it shouldn't.
Theoretically, that's an easy fix: we can just remove one of the $ signs in our application, and let
the user contribute their own. What this means is that we interpolate a quoted version of the
expression entered by the user (which we've already quoted and interpolated once). This leads to
the following code, which can be a little confusing at first, due to the multiple nested levels of
quoting and unquoting. Try to read and understand what each escape is for.
julia> eval(ans)
:(1 / 2 + 1 / 4)
Things started well, but something has gone wrong. The macro's generated code is trying to
interpolate the copy of y in the macro invocation scope; but there is no copy of y in the macro
invocation scope. Our error is allowing interpolation with the second and third arguments in the
macro. To fix this error, we must use QuoteNode.
julia> eval(ans)
:(1 / 2 + 1 / 4)
https://riptutorial.com/ 88
y = 1 # REPL[129], line 5:
$(Expr(:quote, :($(Expr(:$, :($(Expr(:quote, :($(Expr(:$, :y)))))))) + $(Expr(:$,
:($(Expr(:quote, :($(Expr(:$, :y)))))))))))
end
julia> eval(ans)
:(1 + 1)
julia> eval(ans)
:(1 / 2 + 1)
By using QuoteNode, we have protected our arguments from interpolation. Since QuoteNode only has
the effect of additional protections, it is never harmful to use QuoteNode, unless you desire
interpolation. However, understanding the difference makes it possible to understand where and
why Meta.quot could be a better choice.
This long exercise is with an example that is plainly too complex to show up in any reasonable
application. Therefore, we have justified the following rule of thumb, mentioned earlier:
Guide
https://riptutorial.com/ 89
• Prefer to let the code illustrate/demonstrate the concepts rather than paragraphs of
explanation
Resources:
julialang.org
wikibook (@Cormullion)
5 layers (Leah Hanson)
SO-Doc Quoting (@TotalVerb)
SO-Doc -- Symbols that are not legal identifiers (@TotalVerb)
SO: What is a Symbol in Julia (@StefanKarpinski)
Discourse thread (@p-i-) Metaprogramming
Most of the material has come from the discourse channel, most of that has come from fcard...
please prod me if I had forgotten attributions.
Symbol
julia> myName = 42
42
julia> mySymbol |> eval # 'foo |> bar' puts output of 'foo' into 'bar', so 'bar(foo)'
42
julia> myName
1
function dothing(flag)
if flag == :thing_one
println("did thing one")
elseif flag == :thing_two
println("did thing two")
end
end
julia> dothing(:thing_one)
did thing one
julia> dothing(:thing_two)
did thing two
A hashkey example:
https://riptutorial.com/ 90
number_names = Dict{Symbol, Int}()
number_names[:one] = 1
number_names[:two] = 2
number_names[:six] = 6
(Advanced) (@fcard) :foo a.k.a. :(foo) yields a symbol if foo is a valid identifier, otherwise an
expression.
julia> eval(one_plus_one)
ERROR: UndefVarError: 1 + 1 not defined
...
julia> eval(quote
$valid_math
@show($one_plus_one_plus_two)
end)
1 + 1 + 2 = 5
...
Basically you can treat Symbols as lightweight strings. That's not what they're for, but you can do
it, so why not. Julia's Base itself does it, print_with_color(:red, "abc") prints a red-colored abc .
Expr (AST)
(Almost) everything in Julia is an expression, i.e. an instance of Expr, which will hold an AST.
# An Expr instance holds an AST (Abstract Syntax Tree). Let's look at it:
julia> dump(ast)
Expr
https://riptutorial.com/ 91
head: Symbol call
args: Array{Any}((3,))
1: Symbol +
2: Int64 1
3: Int64 1
typ: Any
# TRY: fieldnames(typeof(ast))
Nesting Exprs:
https://riptutorial.com/ 92
# ... unlike:
julia> noDbg = :( x=10; x+1 )
quote
x = 10
x + 1
end
... so quote is functionally the same but provides extra debug info.
:($(foo)) == foo. $(:(foo)) is an error. $(...) isn't an operation and doesn't do anything by itself,
it's an "interpolate this!" sign that the quoting syntax uses. i.e. It only exists within a quote.
eval(:<expr>) should return the same as just <expr> (assuming <expr> is a valid expression in the
current global space)
eval(:(1 + 2)) == 1 + 2
https://riptutorial.com/ 93
macro s
Ready? :)
macro log(x)
:(
println( "Expression: ", $(string(x)), " has value: ", $x )
)
end
u = 42
f = x -> x^2
@log(u) # Expression: u has value: 42
@log(42) # Expression: 42 has value: 42
@log(f(42)) # Expression: f(42) has value: 1764
@log(:u) # Expression: :u has value: u
https://riptutorial.com/ 94
# AST -> (still nonlowered-)AST but with macros expanded:
julia> macroexpand(:(@show x))
quote
(Base.println)("x = ",(Base.repr)(begin # show.jl, line 229:
#28#value = x
end))
#28#value
end
esc()
esc(x)returns an Expr that says "don't apply hygiene to this", it's the same as Expr(:escape, x).
Hygiene is what keeps a macro self-contained, and you esc things if you want them to "leak". e.g.
macro swap(p, q)
quote
tmp = $(esc(p))
$(esc(p)) = $(esc(q))
$(esc(q)) = tmp
end
end
x,y = 1,2
@swap(x,y)
println(x,y) # 2 1
$ allows us to 'escape out of' the quote. So why not simply $p and $q? i.e.
# FAIL!
tmp = $p
$p = $q
$q = tmp
Because that would look first to the macro scope for p, and it would find a local p i.e. the parameter
p (yes, if you subsequently access p without esc-ing, the macro considers the p parameter as a
local variable).
So $p = ... is just a assigning to the local p. it's not affecting whatever variable was passed-in in
the calling context.
Ok so how about:
# Almost!
tmp = $p # <-- you might think we don't
$(esc(p)) = $q # need to esc() the RHS
$(esc(q)) = tmp
So esc(p) is 'leaking' p into the calling context. "The thing that was passed into the macro that we
receive as p"
https://riptutorial.com/ 95
julia> macro swap(p, q)
quote
tmp = $p
$(esc(p)) = $q
$(esc(q)) = tmp
end
end
@swap (macro with 1 method)
julia> x, y = 1, 2
(1,2)
As you can see tmp gets the hygiene treatment #10#tmp, whereas x and y don't. Julia is making a
unique identifier for tmp, something you can manually do with gensym, ie:
julia> gensym(:tmp)
Symbol("##tmp#270")
macro swap(p, q)
quote
tmp = $p
$(esc(p)) = $q
$(esc(q)) = tmp
end
end
end
Swap
julia> @swap(x,y)
ERROR: UndefVarError: x not defined
Another thing julia's macro hygiene does is, if the macro is from another module, it makes any
variables (that were not assigned inside the macro's returning expression, like tmp in this case)
globals of the current module, so $p becomes Swap.$p, likewise $q -> Swap.$q.
https://riptutorial.com/ 96
In general, if you need a variable that is outside the macro's scope you should esc it, so you
should esc(p) and esc(q) regardless if they are on the LHS or RHS of a expression, or even by
themselves.
people have already mentioned gensyms a few times and soon you will be seduced by the dark side
of defaulting to escaping the whole expression with a few gensyms peppered here and there, but...
Make sure to understand how hygiene works before trying to be smarter than it! It's not a
particularly complex algorithm so it shouldn't take too long, but don't rush it! Don't use that power
until you understand all the ramifications of it... (@fcard)
"until loop"
macro until(condition, block)
quote
while ! $condition
$block
end
end |> esc
end
(@fcard) |> is controversial, however. I am surprised a mob hasn't come to argue yet. (maybe
everyone is just tired of it). There is a recommendation of having most if not all of the macro just
be a call to a function, so:
macro swaps(e)
e.args[2:3] = e.args[3:-1:2]
e
end
https://riptutorial.com/ 97
@swaps(1/2)
2.00
macro assert(ex)
return :( $ex ? nothing : throw(AssertionError($(string(ex)))) )
end
Q: Why the last $? A: It interpolates, i.e. forces Julia to eval that string(ex) as execution passes
through the invocation of this macro. i.e. If you just run that code it won't force any evaluation. But
the moment you do assert(foo) Julia will invoke this macro replacing its 'AST token/Expr' with
whatever it returns, and the $ will kick into action.
(@fcard) I don't think there is anything technical keeping {} from being used as blocks, in fact one
can even pun on the residual {} syntax to make it work:
julia> @c {
print(1)
print(2)
1+2
}
123
So first Julia sees the macro token, so it will read/parse tokens until the matching end, and
create what? An Expr with .head=:macro or something? Does it store "a+1" as a string or does
it break it apart into :+(:a, 1)? How to view?
(@fcard) In this case because of lexical scope, a is undefined in @Ms scope so it uses the global
variable... I actually forgot to escape the flipplin' expression in my dumb example, but the "only
works within the same module" part of it still applies.
https://riptutorial.com/ 98
julia> module M
macro m()
:(a+1)
end
end
M
julia> a = 1
1
julia> M.@m
ERROR: UndefVarError: a not defined
The reason being that, if the macro is used in any module other than the one it was defined in, any
variables not defined within the code-to-be-expanded are treated as globals of the macro's
module.
julia> macroexpand(:(M.@m))
:(M.a + 1)
ADVANCED
###@Ismael-VC
@eval begin
"do-until loop"
macro $(:do)(block, until::Symbol, condition)
until ≠ :until &&
error("@do expected `until` got `$until`")
quote
let
$block
@until $condition begin
$block
end
end
end |> esc
end
end
julia> i = 0
0
Scott's macro:
https://riptutorial.com/ 99
"""
Internal function to return captured line number information from AST
##Parameters
- a: Expression in the julia type Expr
##Return
- Line number in the file where the calling macro was invoked
"""
_lin(a::Expr) = a.args[2].args[1].args[1]
"""
Internal function to return captured file name information from AST
##Parameters
- a: Expression in the julia type Expr
##Return
- The name of the file where the macro was invoked
"""
_fil(a::Expr) = string(a.args[2].args[1].args[2])
"""
Internal function to determine if a symbol is a status code or variable
"""
function _is_status(sym::Symbol)
sym in (:OK, :WARNING, :ERROR) && return true
str = string(sym)
length(str) > 4 && (str[1:4] == "ERR_" || str[1:5] == "WARN_" || str[1:5] == "INFO_")
end
"""
Internal function to return captured error code from AST
##Parameters
- a: Expression in the julia type Expr
##Return
- Error code from the captured info in the AST from the calling macro
"""
_err(a::Expr) =
(sym = a.args[2].args[2] ; _is_status(sym) ? Expr(:., :Status, QuoteNode(sym)) : sym)
"""
Internal function to produce a call to the log function based on the macro arguments and the
AST from the ()->ERRCODE anonymous function definition used to capture error code, file name
and line number where the macro is used
##Parameters
- level: Loglevel which has to be logged with macro
- a: Expression in the julia type Expr
- msgs: Optional message
##Return
- Statuscode
"""
function _log(level, a, msgs)
if isempty(msgs)
:( log($level, $(esc(:Symbol))($(_fil(a))), $(_lin(a)), $(_err(a)) )
else
https://riptutorial.com/ 100
:( log($level, $(esc(:Symbol))($(_fil(a))), $(_lin(a)), $(_err(a)),
message=$(esc(msgs[1]))) )
end
end
(@p-i-) Suppose I just do macro m(); a+1; end in a fresh REPL. With no a defined. How can I ‘view’
it? like, is there some way to ‘dump’ a macro? Without actually executing it
(@fcard) All the code in macros are actually put into functions, so you can only view their lowered
or type-inferred code.
julia> @code_typed @m
LambdaInfo for @m()
:(begin
return Main.a + 1
end)
julia> @code_lowered @m
CodeInfo(:(begin
nothing
return Main.a + 1
end))
# ^ or: code_lowered(eval(Symbol("@m")))[1] # ouf!
julia> @getmacro @m
@m (macro with 1 method)
julia> getmacro(:@m)
@m (macro with 1 method)
julia> eval(Symbol("@M"))
@M (macro with 1 method)
https://riptutorial.com/ 101
1-element Array{Any,1}:
LambdaInfo for @M()
julia> @code_typed @M
LambdaInfo for @M()
:(begin
return $(Expr(:copyast, :($(QuoteNode(:(a + 1))))))
end::Expr)
(@fcard) Currently, every macro has a function associated with it. If you have a macro called M,
then the macro's function is called @M. Generally you can get a function's value with e.g.
eval(:print) but with a macro's function you need to do Symbol("@M"), since just :@M becomes an
Expr(:macrocall, Symbol("@M")) and evaluating that causes a macro-expansion.
^ here I see one ::Any param, but it doesn't seem to be connected with the token x.
^ similarly here; there is nothing to connect io with the ::IO So surely this can't be a complete
dump of the AST representation of that particular print method…?
(@fcard) print(::IO, ::Char) only tells you what method it is, it's not part of the AST. It isn't even
present in master anymore:
julia> code_typed(print)[1]
CodeInfo(:(begin
(Base.write)(io,c)
https://riptutorial.com/ 102
return Base.nothing
end))=>Void
(@p-i-) I don't understand what you mean by that. It seems to be dumping the AST for the body of
that method, no? I thought code_typed gives the AST for a function. But it seems to be missing the
first step, i.e. setting up tokens for params.
(@fcard) code_typed is meant to only show the body's AST, but for now it does give the complete
AST of the method, in the form of a LambdaInfo (0.5) or CodeInfo (0.6), but a lot of the information is
omitted when printed to the repl. You will need to inspect the LambdaInfo field by field in order to get
all the details. dump is going to flood your repl, so you could try:
macro method_info(call)
quote
method = @code_typed $(esc(call))
print_info_fields(method)
end
end
function print_info_fields(method)
for field in fieldnames(typeof(method))
if isdefined(method, field) && !(field in [Symbol(""), :code])
println(" $field = ", getfield(method, field))
end
end
display(method)
end
print_info_fields(x::Pair) = print_info_fields(x[1])
Which gives all the values of the named fields of a method's AST:
https://riptutorial.com/ 103
See the lil' def = print(io::IO, c::Char)? There you go! (also the slotnames = [..., :io, :c] part)
Also yes, the difference in output is because I was showing the results on master.
???
(@Ismael-VC) you mean like this? Generic dispatch with Symbols
julia> dispatchtest(:Foo)
This is the generic dispatch. The algorithm is Foo
julia> dispatchtest(:Euler)
This is for the Euler algorithm! I wonder what does @fcard thinks about generic symbol dispatch! -
--^ :angel:
Module Gotcha
@def m begin
a+2
end
More accurately, only works within the toplevel of the module the macro was defined in.
julia> module M
macro m1()
a+1
end
end
M
julia> a = 1
1
https://riptutorial.com/ 104
julia> M.@m1
ERROR: UndefVarError: a not defined
julia> @m2
2
julia> let a = 20
@m2
end
2
esckeeps this from happening, but defaulting to always using it goes against the language design.
A good defense for this is to keep one from using and introducing names within macros, which
makes them hard to track to a human reader.
Introduction
Julia uses the following syntax for dictionaries:
For illustrative purposes we could also use this syntax in Julia and add new semantics to it (Dict
syntax is the idiomatic way in Julia, which is recommended).
This means we need to take this :cell1d expression and either transform it or return a new
expression that should look like this:
Macro definition
The following macro, while simple, allows to demonstrate such code generation and
transformation:
macro dict(expr)
# Check the expression has the correct form:
https://riptutorial.com/ 105
if expr.head ≠ :cell1d || any(sub_expr.head ≠ :(:) for sub_expr ∈ expr.args)
error("syntax: expected `{k₁: v₁, k₂: v₂, …, k ₋₁: v ₋₁, k : v }`")
end
Usage
julia> @dict {
"string": :b,
'c' : 1,
:symbol : π,
Function: print,
(1:10) : range(1, 10)
}
Dict{Any,Any} with 5 entries:
1:10 => 1:10
Function => print
"string" => :b
:symbol => π = 3.1415926535897...
'c' => 1
Dict(
"string" => :b,
'c' => 1,
:symbol => π,
Function => print,
(1:10) => range(1, 10)
)
https://riptutorial.com/ 106
Misusage
Notice that Julia has other uses for colon : as such you will need to wrap range literal expressions
with parenthesis or use the range function, for example.
https://riptutorial.com/ 107
Chapter 21: Modules
Syntax
• module Module; ...; end
• using Module
• import Module
Examples
Wrap Code in a Module
The module keyword can be used to begin a module, which allows code to be organized and
namespaced. Modules can define an external interface, typically consisting of exported symbols.
To support this external interface, modules can have unexported internal functions and types not
intended for public use.
Some modules primarily exist to wrap a type and associated functions. Such modules, by
convention, are usually named with the plural form of the type's name. For instance, if we have a
module that provides a Building type, we can call such a module Buildings.
module Buildings
immutable Building
name::String
stories::Int
height::Int # in metres
end
name(b::Building) = b.name
stories(b::Building) = b.stories
height(b::Building) = b.height
end
julia> height(ans)
830
https://riptutorial.com/ 108
Using Modules to Organize Packages
Typically, packages consist of one or more modules. As packages grow, it may be useful to
organize the main module of the package into smaller modules. A common idiom is to define
those modules as submodules of the main module:
module RootModule
module SubModule1
...
end
module SubModule2
...
end
end
Initially, neither root module nor submodules have access to each others' exported symbols.
However, relative imports are supported to address this issue:
module RootModule
module SubModule1
const x = 10
export x
end
module SubModule2
end
end
https://riptutorial.com/ 109
Chapter 22: Packages
Syntax
• Pkg.add(package)
• Pkg.checkout(package, branch="master")
• Pkg.clone(url)
• Pkg.dir(package)
• Pkg.pin(package, version)
• Pkg.rm(package)
Parameters
Parameter Details
Pkg.checkout(package, Check out the given branch for the given registered package. branch
branch) is optional and defaults to "master".
Pkg.pin(package, Force the package to remain at the given version. version is optional
version) and defaults to the current version of the package.
Pkg.rm(package) Remove the given package from the list of required packages.
Examples
Install, use, and remove a registered package
After finding an official Julia package, it is straightforward to download and install the package.
Firstly, it's recommended to refresh the local copy of METADATA:
julia> Pkg.update()
This will ensure that you get the latest versions of all packages.
Suppose that the package we want to install is named Currencies.jl. The command to run to
install this package would be:
julia> Pkg.add("Currencies")
https://riptutorial.com/ 110
This command will install not only the package itself, but also all of its dependencies.
If the installation is successful, you can test that the package works properly:
julia> Pkg.test("Currencies")
and proceed as described by the package's documentation, usually linked to or included from its
README.md file.
julia> Pkg.rm("Currencies")
Note that this may not actually remove the package directory; instead it will merely mark the
package as no longer required. Often, this is perfectly fine — it will save time in case you need the
package again in the future. But if necessary, to remove the package physically, call the rm
function, then call Pkg.resolve:
julia> Pkg.resolve()
Sometimes, the latest tagged version of a package is buggy or is missing some required features.
Advanced users may wish to update to the latest development version of a package (sometimes
referred to as the "master", named after the usual name for a development branch in Git). The
benefits of this include:
However, there are many drawbacks to running the latest development version:
• The latest development version may be poorly-tested and have serious bugs.
• The latest development version can change frequently, breaking your code.
To check out the latest development branch of a package named JSON.jl, for example, use
Pkg.checkout("JSON")
https://riptutorial.com/ 111
Pkg.checkout("JSON", "v0.6.0")
Pkg.pin("JSON", v"0.6.0")
Note that a version literal is used here, not a plain string. The Pkg.pin version informs the package
manager of the version constraint, allowing the package manager to offer feedback on what
problems it might cause.
Pkg.free("JSON")
Some experimental packages are not included in the METADATA package repository. These
packages can be installed by directly cloning their Git repositories. Note that there may be
dependencies of unregistered packages that are themselves unregistered; those dependencies
cannot be resolved by the package manager and must be resolved manually. For example, to
install the unregistered package OhMyREPL.jl:
Pkg.clone("https://github.com/KristofferC/Tokenize.jl")
Pkg.clone("https://github.com/KristofferC/OhMyREPL.jl")
using OhMyREPL
https://riptutorial.com/ 112
Chapter 23: Parallel Processing
Examples
pmap
pmap takes a function (that you specify) and applies it to all of the elements in an array. This work is
divided up amongst the available workers. pmap then returns places the results from that function
into another array.
addprocs(3)
sqrts = pmap(sqrt, 1:10)
if you function takes multiple arguments, you can supply multiple vectors to pmap
As with @parallel, however, if the function given to pmap is not in base Julia (i.e. it is user-defined or
defined in a package) then you must make sure that function is available to all workers first:
@everywhere begin
function rand_det(n)
det(rand(n,n))
end
end
@parallel
@parallel can be used to parallellize a loop, dividing steps of the loop up over different workers.
As a very simple example:
addprocs(3)
a = collect(1:10)
@time begin
@sync begin
@parallel for idx in 1:length(a)
https://riptutorial.com/ 113
sleep(a[idx])
end
end
end
27.023411 seconds (13.48 k allocations: 762.532 KB)
julia> sum(a)
55
Thus, we see that if we had executed this loop without @parallel it would have taken 55 seconds,
rather than 27, to execute.
We can also supply a reduction operator for the @parallel macro. Suppose we have an array, we
want to sum each column of the array and then multiply these sums by each other:
A = rand(100,100);
There are several important things to keep in mind when using @parallel to avoid unexpected
behavior.
First: if you want to use any functions in your loops that are not in base Julia (e.g. either functions
you define in your script or that you import from packages), then you must make those functions
accessible to the workers. Thus, for example, the following would not work:
myprint(x) = println(x)
for idx = 1:10
myprint(a[idx])
end
@everywhere begin
function myprint(x)
println(x)
end
end
Second Although each worker will be able to access the objects in the scope of the controller,
they will not be able to modify them. Thus
a = collect(1:10)
@parallel for idx = 1:length(a)
a[idx] += 1
end
julia> a'
https://riptutorial.com/ 114
1x10 Array{Int64,2}:
1 2 3 4 5 6 7 8 9 10
Whereas, if we had executed the loop wihtout the @parallel it would have successfully modified
the array a.
TO ADDRESS THIS, we can instead make a a SharedArray type object so that each worker can
access and modify it:
a = convert(SharedArray{Float64,1}, collect(1:10))
@parallel for idx = 1:length(a)
a[idx] += 1
end
julia> a'
1x10 Array{Float64,2}:
2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0
The macros @spawn and @spawnat are two of the tools that Julia makes available to assign tasks to
workers. Here is an example:
Both of these macros will evaluate an expression on a worker process. The only difference
between the two is that @spawnat allows you to choose which worker will evaluate the expression
(in the example above worker 2 is specified) whereas with @spawn a worker will be automatically
chosen, based on availability.
In the above example, we simply had worker 2 execute the println function. There was nothing of
interest to return or retrieve from this. Often, however, the expression we sent to the worker will
yield something we wish to retrieve. Notice in the example above, when we called @spawnat, before
we got the printout from worker 2, we saw the following:
RemoteRef{Channel{Any}}(2,1,3)
This indicates that the @spawnat macro will return a RemoteRef type object. This object in turn will
contain the return values from our expression that is sent to the worker. If we want to retrieve
those values, we can first assign the RemoteRef that @spawnat returns to an object and then, and
then use the fetch() function which operates on a RemoteRef type object, to retrieve the results
stored from an evaluation performed on a worker.
julia> fetch(result)
https://riptutorial.com/ 115
7
The key to being able to use @spawn effectively is understanding the nature behind the expressions
that it operates on. Using @spawn to send commands to workers is slightly more complicated than
just typing directly what you would type if you were running an "interpreter" on one of the workers
or executing code natively on them. For instance, suppose we wished to use @spawnat to assign a
value to a variable on a worker. We might try:
@spawnat 2 a = 5
RemoteRef{Channel{Any}}(2,1,2)
julia>
Nothing happened. Why? We can investigate this more by using fetch() as above. fetch() can be
very handy because it will retrieve not just successful results but also error messages as well.
Without it, we might not even know that something has gone wrong.
julia> fetch(result)
ERROR: On worker 2:
UndefVarError: a not defined
The error message says that a is not defined on worker 2. But why is this? The reason is that we
need to wrap our assignment operation into an expression that we then use @spawn to tell the
worker to evaluate. Below is an example, with explanation following:
The :() syntax is what Julia uses to designate expressions. We then use the eval() function in
Julia, which evaluates an expression, and we use the @spawnat macro to instruct that the
expression be evaluated on worker 2.
https://riptutorial.com/ 116
RemoteRef{Channel{Any}}(2,1,10)
This example demonstrates two additional notions. First, we see that we can also create an
expression using the parse() function called on a string. Secondly, we see that we can use
parentheses when calling @spawnat, in situations where this might make our syntax more clear and
manageable.
pmap() is designed for the case where each function call does a large amount of work.
In contrast, @parallel for can handle situations where each iteration is tiny, perhaps
merely summing two numbers.
There are several reasons for this. First, pmap incurs greater start up costs initiating jobs on
workers. Thus, if the jobs are very small, these startup costs may become inefficient. Conversely,
however, pmap does a "smarter" job of allocating jobs amongst workers. In particular, it builds a
queue of jobs and sends a new job to each worker whenever that worker becomes available.
@parallel by contrast, divvies up all work to be done amongst the workers when it is called. As
such, if some workers take longer on their jobs than others, you can end up with a situation where
most of your workers have finished and are idle while a few remain active for an inordinate amount
of time, finishing their jobs. Such a situation, however, is less likely to occur with very small and
simple jobs.
The following illustrates this: suppose we have two workers, one of which is slow and the other of
which is twice as fast. Ideally, we would want to give the fast worker twice as much work as the
slow worker. (or, we could have fast and slow jobs, but the principal is the exact same). pmap will
accomplish this, but @parallel won't.
addprocs(2)
@everywhere begin
function parallel_func(idx)
workernum = myid() - 1
sleep(workernum)
println("job $idx")
end
end
https://riptutorial.com/ 117
And get back print output:
It's almost sweet. The workers have "shared" the work evenly. Note that each worker has
completed 6 jobs, even though worker 2 is twice as fast as worker 3. It may be touching, but it is
inefficient.
pmap(parallel_func, 1:12)
Now, note that worker 2 has performed 8 jobs and worker 3 has performed 4. This is exactly in
proportion to their speed, and what we want for optimal efficiency. pmap is a hard task master -
from each according to their ability.
According to the documentation under ?@async, "@async wraps an expression in a Task." What this
means is that for whatever falls within its scope, Julia will start this task running but then proceed
to whatever comes next in the script without waiting for the task to complete. Thus, for instance,
without the macro you will get:
https://riptutorial.com/ 118
But with the macro, you get:
julia>
Julia thus allows the script to proceed (and the @time macro to fully execute) without waiting for the
task (in this case, sleeping for two seconds) to complete.
The @sync macro, by contrast, will "Wait until all dynamically-enclosed uses of @async, @spawn,
@spawnat and @parallel are complete." (according to the documentation under ?@sync). Thus, we
see:
In this simple example then, there is no point to including a single instance of @async and @sync
together. But, where @sync can be useful is where you have @async applied to multiple operations
that you wish to allow to all start at once without waiting for each to complete.
For example, suppose we have multiple workers and we'd like to start each of them working on a
task simultaneously and then fetch the results from those tasks. An initial (but incorrect) attempt
might be:
addprocs(2)
@time begin
a = cell(nworkers())
for (idx, pid) in enumerate(workers())
a[idx] = remotecall_fetch(pid, sleep, 2)
end
end
## 4.011576 seconds (177 allocations: 9.734 KB)
The problem here is that the loop waits for each remotecall_fetch() operation to finish, i.e. for each
process to complete its work (in this case sleeping for 2 seconds) before continuing to start the
next remotecall_fetch() operation. In terms of a practical situation, we're not getting the benefits of
parallelism here, since our processes aren't doing their work (i.e. sleeping) simultaneously.
We can correct this, however, by using a combination of the @async and @sync macros:
@time begin
a = cell(nworkers())
@sync for (idx, pid) in enumerate(workers())
@async a[idx] = remotecall_fetch(pid, sleep, 2)
end
end
## 2.009416 seconds (274 allocations: 25.592 KB)
Now, if we count each step of the loop as a separate operation, we see that there are two
https://riptutorial.com/ 119
separate operations preceded by the @async macro. The macro allows each of these to start up,
and the code to continue (in this case to the next step of the loop) before each finishes. But, the
use of the @sync macro, whose scope encompasses the whole loop, means that we won't allow the
script to proceed past that loop until all of the operations preceded by @async have completed.
It is possible to get an even more clear understanding of the operation of these macros by further
tweaking the above example to see how it changes under certain modifications. For instance,
suppose we just have the @async without the @sync:
@time begin
a = cell(nworkers())
for (idx, pid) in enumerate(workers())
println("sending work to $pid")
@async a[idx] = remotecall_fetch(pid, sleep, 2)
end
end
## 0.001429 seconds (27 allocations: 2.234 KB)
Here, the @async macro allows us to continue in our loop even before each remotecall_fetch()
operation finishes executing. But, for better or worse, we have no @sync macro to prevent the code
from continuing past this loop until all of the remotecall_fetch() operations finish.
Nevertheless, each remotecall_fetch() operation is still running in parallel, even once we go on.
We can see that because if we wait for two seconds, then the array a, containing the results, will
contain:
sleep(2)
julia> a
2-element Array{Any,1}:
nothing
nothing
(The "nothing" element is the result of a successful fetch of the results of the sleep function, which
does not return any values)
We can also see that the two remotecall_fetch() operations start at essentially the same time
because the print commands that precede them also execute in rapid succession (output from
these commands not shown here). Contrast this with the next example where the print commands
execute at a 2 second lag from each other:
If we put the @async macro on the whole loop (instead of just the inner step of it), then again our
script will continue immediately without waiting for the remotecall_fetch() operations to finish. Now,
however, we only allow for the script to continue past the loop as a whole. We don't allow each
individual step of the loop to start before the previous one finished. As such, unlike in the example
above, two seconds after the script proceeds after the loop, the results array still has one element
as #undef indicating that the second remotecall_fetch() operation still has not completed.
@time begin
a = cell(nworkers())
@async for (idx, pid) in enumerate(workers())
println("sending work to $pid")
https://riptutorial.com/ 120
a[idx] = remotecall_fetch(pid, sleep, 2)
end
end
# 0.001279 seconds (328 allocations: 21.354 KB)
# Task (waiting) @0x0000000115ec9120
## This also allows us to continue to
sleep(2)
a
2-element Array{Any,1}:
nothing
#undef
And, not surprisingly, if we put the @sync and @async right next to each other, we get that each
remotecall_fetch() runs sequentially (rather than simultaneously) but we don't continue in the code
until each has finished. In other words, this would be essentially the equivalent of if we had neither
macro in place, just like sleep(2) behaves essentially identically to @sync @async sleep(2)
@time begin
a = cell(nworkers())
@sync @async for (idx, pid) in enumerate(workers())
a[idx] = remotecall_fetch(pid, sleep, 2)
end
end
# 4.019500 seconds (4.20 k allocations: 216.964 KB)
# Task (done) @0x0000000115e52a10
Note also that it is possible to have more complicated operations inside the scope of the @async
macro. The documentation gives an example containing an entire loop within the scope of @async.
Recall that the help for the sync macros states that it will "Wait until all dynamically-enclosed uses
of @async, @spawn, @spawnat and @parallel are complete." For the purposes of what counts as
"complete" it matters how you define the tasks within the scope of the @sync and @async macros.
Consider the below example, which is a slight variation on one of the examples given above:
@time begin
a = cell(nworkers())
@sync for (idx, pid) in enumerate(workers())
@async a[idx] = remotecall(pid, sleep, 2)
end
end
## 0.172479 seconds (93.42 k allocations: 3.900 MB)
julia> a
2-element Array{Any,1}:
RemoteRef{Channel{Any}}(2,1,3)
RemoteRef{Channel{Any}}(3,1,4)
The earlier example took roughly 2 seconds to execute, indicating that the two tasks were run in
parallel and that the script waiting for each to complete execution of their functions before
proceeding. This example, however, has a much lower time evaluation. The reason is that for the
purposes of @sync the remotecall() operation has "finished" once it has sent the worker the job to
do. (Note that the resulting array, a, here, just contains RemoteRef object types, which just indicate
https://riptutorial.com/ 121
that there is something going on with a particular process which could in theory be fetched at
some point in the future). By contrast, the remotecall_fetch() operation has only "finished" when it
gets the message from the worker that its task is complete.
Thus, if you are looking for ways to ensure that certain operations with workers have completed
before moving on in your script (as for instance is discussed in this post) it is necessary to think
carefully about what counts as "complete" and how you will measure and then operationalize that
in your script.
Adding Workers
When you first start Julia, by default, there will only be a single process running and available to
give work to. You can verify this using:
julia> nprocs()
1
In order to take advantage of parallel processing, you must first add additional workers who will
then be available to do work that you assign to them. You can do this within your script (or from
the interpreter) using: addprocs(n) where n is the number of processes you want to use.
Alternatively, you can add processes when you start Julia from the command line using:
$ julia -p n
where n is how many additional processes you want to add. Thus, if we start Julia with
$ julia -p 2
julia> nprocs()
3
https://riptutorial.com/ 122
Chapter 24: Reading a DataFrame from a file
Examples
Reading a dataframe from delimiter separated data
You may want to read a DataFrame from a CSV (Comma separated values) file or maybe even from
a TSV or WSV (tabs and whitespace separated files). If your file has the right extension, you can
use the readtable function to read in the dataframe:
readtable("dataset.CSV")
But what if your file doesn't have the right extension? You can specify the delimiter that your file
uses (comma, tab, whitespace etc) as a keyword argument to the readtable function:
readtable("dataset.txt", separator=',')
Data sets often contain comments that explain the data format or contain the license and usage
terms. You usually want to ignore these lines when you read in the DataFrame.
The readtable function assumes that comment lines begin with the '#' character. However, your file
may use comment marks like % or //. To make sure that readtable handles these correctly, you can
specify the comment mark as a keyword argument:
https://riptutorial.com/ 123
Chapter 25: Regexes
Syntax
• Regex("[regex]")
• r"[regex]"
• match(needle, haystack)
• matchall(needle, haystack)
• eachmatch(needle, haystack)
• ismatch(needle, haystack)
Parameters
Parameter Details
Examples
Regex literals
Julia supports regular expressions1. The PCRE library is used as the regex implementation.
Regexes are like a mini-language within a language. Since most languages and many text editors
provide some support for regex, documentation and examples of how to use regex in general are
outside the scope of this example.
julia> Regex("(cat|dog)s?")
But for convenience and easier escaping, the @r_str string macro can be used instead:
julia> r"(cat|dog)s?"
1:Technically, Julia supports regexes, which are distinct from and more powerful than what are
called regular expressions in language theory. Frequently, the term "regular expression" will be
used to refer to regexes also.
Finding matches
There are four primary useful functions for regular expressions, all of which take arguments in
needle, haystack order. The terminology "needle" and "haystack" come from the English idiom
https://riptutorial.com/ 124
"finding a needle in a haystack". In the context of regexes, the regex is the needle, and the text is
the haystack.
The match function can be used to find the first match in a string:
The matchall function can be used to find all matches of a regular expression in a string:
The ismatch function returns a boolean indicating whether a match was found inside the string:
The eachmatch function returns an iterator over RegexMatch objects, suitable for use with for loops:
Capture groups
The substrings captured by capture groups are accessible from RegexMatch objects using indexing
notation.
For instance, the following regex parses North American phone numbers written in (555)-555-5555
format:
Using the matchall function, we can get an array of the substrings matched themselves:
https://riptutorial.com/ 125
julia> matchall(phone, text)
2-element Array{SubString{String},1}:
"(555)-505-1000"
"(555)-999-9999"
But suppose we want to access the area codes (the first three digits, enclosed in brackets). Then
we can use the eachmatch iterator:
Note here that we use m[1] because the area code is the first capture group in our regular
expression. We can get all three components of the phone number as a tuple using a function:
https://riptutorial.com/ 126
Chapter 26: REPL
Syntax
• julia>
• help?>
• shell>
• \[latex]
Remarks
Other packages may define their own REPL modes in addition to the default modes. For instance,
the Cxx package defines the cxx> shell mode for a C++ REPL. These modes are usually accessible
with their own special keys; see package documentation for more details.
Examples
Launch the REPL
On Unix Systems
Open a terminal window, then type julia at the prompt, then hit Return. You should see something
like this come up:
On Windows
Find the Julia program in your start menu, and click it. The REPL should be launched.
The Julia REPL is an excellent calculator. We can start with some simple operations:
julia> 1 + 1
https://riptutorial.com/ 127
2
julia> 8 * 8
64
julia> 9 ^ 2
81
julia> 4 + 9
13
julia> ans + 9
22
julia> x = 10
10
julia> y = 20
20
julia> x + y
30
Julia has implicit multiplication for numeric literals, which makes some calculations quicker to
write:
julia> 10x
100
julia> 2(x + y)
60
If we make a mistake and do something that is not allowed, the Julia REPL will throw an error,
often with a helpful tip on how to fix the problem:
julia> 1 ^ -1
ERROR: DomainError:
Cannot raise an integer x to a negative power -n.
Make x a float by adding a zero decimal (e.g. 2.0^-n instead of 2^-n), or write
1/x^n, float(x)^-n, or (x//1)^-n.
in power_by_squaring at ./intfuncs.jl:82
in ^ at ./intfuncs.jl:106
julia> 1.0 ^ -1
1.0
To access or edit previous commands, use the ↑ (Up) key, which moves to the last item in history.
The ↓ moves to the next item in history. The ← and → keys can be used to move and make edits to
a line.
https://riptutorial.com/ 128
Julia has some built-in mathematical constants, including e and pi (or π).
julia> e
e = 2.7182818284590...
julia> pi
π = 3.1415926535897...
julia> 3π
9.42477796076938
We can type characters like π quickly by using their LaTeX codes: press \, then p and i, then hit
the Tab key to substitute the \pi just typed with π. This works for other Greek letters and additional
unicode symbols.
We can use any of Julia's built-in math functions, which range from simple to fairly powerful:
julia> cos(π)
-1.0
julia> besselh(1, 1, 1)
0.44005058574493355 - 0.7812128213002889im
Some functions will not return a complex result unless you give it a complex input, even if the input
is real:
julia> sqrt(-1)
ERROR: DomainError:
sqrt will only return a complex result if called with a complex argument. Try
sqrt(complex(x)).
in sqrt at math.jl:146
julia> sqrt(-1+0im)
0.0 + 1.0im
julia> sqrt(complex(-1))
0.0 + 1.0im
Exact operations on rational numbers are possible using the // rational division operator:
See the Arithmetic topic for more about what sorts of arithmetic operators are supported by Julia.
https://riptutorial.com/ 129
Note that machine integers are constrained in size, and will overflow if the result is too big to be
stored:
julia> 2^62
4611686018427387904
julia> 2^63
-9223372036854775808
julia> big"2"^62
4611686018427387904
julia> big"2"^63
9223372036854775808
There are three built-in REPL modes in Julia: the Julia mode, the help mode, and the shell mode.
At the help prompt, type the name of some function or type to get help for:
https://riptutorial.com/ 130
Even if you do not spell the function correctly, Julia can suggest some functions that are possibly
what you meant:
help?> printline
search:
This documentation works for other modules too, as long as they use the Julia documentation
system.
help?> @usingcurrencies
Export each given currency symbol into the current namespace. The individual unit
exported will be a full unit of the currency specified, not the smallest possible
unit. For instance, @usingcurrencies EUR will export EUR, a currency unit worth
1€, not a currency unit worth 0.01€.
There is no sane unit for certain currencies like XAU or XAG, so this macro does
not work for those. Instead, define them manually:
https://riptutorial.com/ 131
Chapter 27: Shell Scripting and Piping
Syntax
• ;shell command
Examples
Using Shell from inside the REPL
From inside the interative Julia shell (also known as REPL), you can access the system's shell by
typing ; right after the prompt:
shell>
From here on, you can type any shell comand and they will be run from inside the REPL:
shell> ls
Desktop Documents Pictures Templates
Downloads Music Public Videos
Julia code can create, manipulate, and execute command literals, which execute in the OS's
system environment. This is powerful but often makes programs less portable.
A command literal can be created using the `` literal. Information can be interpolated using the $
interpolation syntax, as with string literals. Julia variables passed through command literals need
not be escaped first; they are not actually passed to the shell, but rather directly to the kernel.
However, Julia displays these objects so that they appear properly escaped.
julia> cd("/directory/where/there/are/unstaged/changes")
julia> run(command)
[master (root-commit) 0945387] add a
4 files changed, 1 insertion(+)
https://riptutorial.com/ 132
Chapter 28: String Macros
Syntax
• macro"string" # short, string macro form
• @macro_str "string" # long, regular macro form
• macro`command`
Remarks
String macros are not quite as powerful as plain old strings — because interpolation must be
implemented in the macro's logic, string macros are unable to contain string literals of the same
delimiter for interpolation.
julia> "$("x")"
"x"
julia> doc"$("x")"
ERROR: KeyError: key :x not found
gets parsed incorrectly. This can be somewhat mitigated by using triple-quotes as the outer string
delimiter;
julia> doc"""$("x")"""
"x"
Examples
Using string macros
String macros are syntactic sugar for certain macro invocations. The parser expands syntax like
mymacro"my string"
into
which then, like any other macro call, gets substituted with whatever expression the @mymacro_str
https://riptutorial.com/ 133
macro returns. Base Julia comes with several string macros, such as:
@b_str
This string macro constructs byte arrays instead of strings. The contents of the string, encoded as
UTF-8, will be used as the array of bytes. This can be useful for interfacing with low-level APIs,
many of which work with byte arrays instead of strings.
@big_str
This macro will return a BigInt or a BigFloat parsed from the string it's given.
julia> big"1"
1
julia> big"1.0"
1.000000000000000000000000000000000000000000000000000000000000000000000000000000
This macro exists because big(0.1) does not behave as one might initially expect: the 0.1 is a
Float64 approximation of true 0.1 (1//10), and promoting that to BigFloat will keep the
approximation error of Float64. Using the macro will parse 0.1 directly to a BigFloat, reducing the
approximation error.
julia> big(0.1)
1.000000000000000055511151231257827021181583404541015625000000000000000000000000e-01
julia> big"0.1"
1.000000000000000000000000000000000000000000000000000000000000000000000000000002e-01
@doc_str
This string macro constructs Base.Markdown.MD objects, which are used in the internal
documentation system to provide rich-text documentation for any environment. These MD objects
render well in a terminal:
https://riptutorial.com/ 134
and also in a browser:
@html_str
This string macro constructs HTML string literals, which render nicely in a browser:
@ip_str
This string macro constructs IP address literals. It works with both IPv4 and IPv6:
julia> ip"127.0.0.1"
ip"127.0.0.1"
julia> ip"::"
ip"::"
@r_str
https://riptutorial.com/ 135
This string macro constructs Regex literals.
@s_str
This string macro constructs SubstitutionString literals, which work together with Regex literals to
allow more advanced textual substitution.
@text_str
This string macro is similar in spirit to @doc_str and @html_str, but does not have any fancy
formatting features:
@v_str
This string macro constructs VersionNumber literals. See Version Numbers for a description of what
they are and how to use them.
@MIME_str
This string macro constructs the singleton types of MIME types. For instance, MIME"text/plain" is
the type of MIME("text/plain").
julia> :cat
:cat
julia> :2cat
ERROR: MethodError: no method matching *(::Int64, ::Base.#cat)
Closest candidates are:
*(::Any, ::Any, ::Any, ::Any...) at operators.jl:288
*{T<:Union{Int128,Int16,Int32,Int64,Int8,UInt128,UInt16,UInt32,UInt64,UInt8}}(::T<:Union{Int128,Int16,I
::T<:Union{Int128,Int16,Int32,Int64,Int8,UInt128,UInt16,UInt32,UInt64,UInt8}) at int.jl:33
*(::Real, ::Complex{Bool}) at complex.jl:180
...
What looks like a symbol literal here is actually being parsed as an implicit multiplication of :2
(which is just 2) and the function cat, which obviously does not work.
We can use
https://riptutorial.com/ 136
julia> Symbol("2cat")
Symbol("2cat")
A string macro could help to make this more terse. If we define the @sym_str macro:
macro sym_str(str)
Meta.quot(Symbol(str))
end
julia> sym"2cat"
Symbol("2cat")
Of course, these techniques can also create symbols that are valid Julia identifiers. For example,
julia> sym"test"
:test
String macros do not come with built-in interpolation facilities. However, it is possible to manually
implement this functionality. Note that it is not possible to embed without escaping string literals
that have the same delimiter as the surrounding string macro; that is, although """ $("x") """ is
possible, " $("x") " is not. Instead, this must be escaped as " $(\"x\") ". See the remarks section
for more details about this limitation.
There are two approaches to implementing interpolation manually: implement parsing manually, or
get Julia to do the parsing. The first approach is more flexible, but the second approach is easier.
Manual parsing
macro interp_str(s)
components = []
buf = IOBuffer(s)
while !eof(buf)
push!(components, rstrip(readuntil(buf, '$'), '$'))
if !eof(buf)
push!(components, parse(buf; greedy=false))
end
end
quote
string($(map(esc, components)...))
end
end
https://riptutorial.com/ 137
Julia parsing
macro e_str(s)
esc(parse("\"$(escape_string(s))\""))
end
This method escapes the string (but note that escape_string does not escape the $ signs) and
passes it back to Julia's parser to parse. Escaping the string is necessary to ensure that " and \ do
not affect the string's parsing. The resulting expression is a :string expression, which can be
examined and decomposed for macro purposes.
Command macros
0.6.0-dev
In Julia v0.6 and later, command macros are supported in addition to regular string macros. A
command macro invocation like
mymacro`xyz`
@mymacro_cmd "xyz"
Note that this is similar to string macros, except with _cmd instead of _str.
We typically use command macros for code, which in many languages frequently contains " but
rarely contains `. For instance, it is fairly straightforward to reimplement a simple version of
quasiquoting using command macros:
macro julia_cmd(s)
esc(Meta.quot(parse(s)))
end
julia> julia`1+1`
:(1 + 1)
julia> julia`hypot2(x,y)=x^2+y^2`
:(hypot2(x,y) = begin # none, line 1:
x ^ 2 + y ^ 2
end)
or multiline:
julia> julia```
function hello()
println("Hello, World!")
https://riptutorial.com/ 138
end
```
:(function hello() # none, line 2:
println("Hello, World!")
end)
julia> x = 2
2
julia> julia```
x = 2
y = 3
```
ERROR: ParseError("extra token after end of expression")
https://riptutorial.com/ 139
Chapter 29: String Normalization
Syntax
• normalize_string(s::String, ...)
Parameters
Parameter Details
casefold=true Fold the string to a canonical case based off the Unicode standard.
stripmark=true Strip diacritical marks (i.e. accents) from characters in the input string.
Examples
Case-Insensitive String Comparison
Strings can be compared with the == operator in Julia, but this is sensitive to differences in case.
For instance, "Hello" and "hello" are considered different strings.
To compare strings in a case-insensitive manner, normalize the strings by case-folding them first.
For example,
equals_ignore_case(s, t) =
normalize_string(s, casefold=true) == normalize_string(t, casefold=true)
Sometimes, one wants strings like "resume" and "résumé" to compare equal. That is, graphemes
https://riptutorial.com/ 140
that share a basic glyph, but possibly differ because of additions to those basic glyphs. Such
comparison can be accomplished by stripping diacritical marks.
equals_ignore_mark(s, t) =
normalize_string(s, stripmark=true) == normalize_string(t, stripmark=true)
This allows the above example to work correctly. Additionally, it works well even with non-ASCII
Unicode characters.
https://riptutorial.com/ 141
Chapter 30: Strings
Syntax
• "[string]"
• '[Unicode scalar value]'
• graphemes([string])
Parameters
Parameter Details
Examples
Hello, World!
Note that unlike some other languages, the ' symbol cannot be used instead. ' defines a
character literal; this is a Char data type and will only store a single Unicode scalar value:
julia> 'c'
'c'
julia> 'character'
ERROR: syntax: invalid character literal
One can extract the unicode scalar values from a string by iterating over it with a for loop:
https://riptutorial.com/ 142
W
o
r
l
d
!
Graphemes
Julia's Char type represents a Unicode scalar value, which only in some cases corresponds to what
humans perceive as a "character". For instance, one representation of the character é, as in
résumé, is actually a combination of two Unicode scalar values:
julia> collect("é")
2-element Array{Char,1}:
'e'
'́'
The Unicode descriptions for these codepoints are "LATIN SMALL LETTER E" and "COMBINING
ACUTE ACCENT". Together, they define a single "human" character, which is Unicode terms is
called a grapheme. More specifically, Unicode Annex #29 motivates the definition of a grapheme
cluster because:
It is important to recognize that what the user thinks of as a “character”—a basic unit of
a writing system for a language—may not be just a single Unicode code point. Instead,
that basic unit may be made up of multiple Unicode code points. To avoid ambiguity
with the computer use of the term character, this is called a user-perceived character.
For example, “G” + acute-accent is a user-perceived character: users think of it as a
single character, yet is actually represented by two Unicode code points. These user-
perceived characters are approximated by what is called a grapheme cluster, which
can be determined programmatically.
Julia provides the graphemes function to iterate over the grapheme clusters in a string:
Note how the result, printing each character on its own line, is better than if we had iterated over
the Unicode scalar values:
https://riptutorial.com/ 143
r
e
s
u
m
e
Typically, when working with characters in a user-perceived sense, it is more useful to deal with
grapheme clusters than with Unicode scalar values. For instance, suppose we want to write a
function to compute the length of a single word. A naïve solution would be to use
We note that the result is counter-intuitive when the word includes grapheme clusters that consist
of more than one codepoint:
julia> wordlength("résumé")
8
When we use the more correct definition, using the graphemes function, we get the expected result:
julia> wordlength("résumé")
6
julia> a = 123
123
julia> string(a)
"123"
julia> println(a)
123
You can also insert (aka interpolate) integers (and certain other types) into strings using $:
https://riptutorial.com/ 144
Performance Tip: The above methods can be quite convenient at times. But, if you will be
performing many, many such operations and you are concerned about execution speed of your
code, the Julia performance guide recommends against this, and instead in favor of the below
methods:
You can supply multiple arguments to print() and println() which will operate on them exactly as
string() operates on multiple arguments:
or
These are faster because they avoid needing to first form a string from given pieces and then
output it (either to the console display or a file) and instead just sequentially output the various
pieces.
Credits: Answer based on SO Question What's the best way to convert an Int to a String in Julia?
with Answer by Michael Ohlrogge and Input from Fengyang Wang
n = 2
julia> MyString = "there are $n ducks"
"there are 2 ducks"
Result = false
julia> println("test results is $Result")
test results is false
MySubStr = "a32"
MyNum = 123.31
https://riptutorial.com/ 145
println("$MySubStr , $MyNum")
Performance Tip Interpolation is quite convenient. But, if you are going to be doing it many times
very rapidly, it is not the most efficient. Instead, see Convert numeric types to strings for
suggestions when performance is an issue.
Strings can be made from functions that work with IO objects by using the sprint function. For
instance, the code_llvm function accepts an IO object as the first argument. Typically, it is used like
julia> println(ans)
Converting the results of "interactive" functions like code_llvm into strings can be useful for
automated analysis, such as testing whether generated code may have regressed.
The sprint function is a higher-order function which takes the function operating on IO objects as
its first argument. Behind the scenes, it creates an IOBuffer in RAM, calls the given function, and
takes the data from the buffer into a String object.
https://riptutorial.com/ 146
Chapter 31: sub2ind
Syntax
• sub2ind(dims::Tuple{Vararg{Integer}}, I::Integer...)
• sub2ind{T<:Integer}(dims::Tuple{Vararg{Integer}}, I::AbstractArray{T<:Integer,1}...)
Parameters
parameter details
Remarks
The second example shows that the result of sub2ind might be very buggy in some specific cases.
Examples
Convert subscripts to linear indices
julia> sub2ind((3,3), 1, 1)
1
julia> sub2ind((3,3), 1, 2)
4
julia> sub2ind((3,3), 2, 1)
2
https://riptutorial.com/ 147
One cannot determine whether a subscript is in the range of an array by comparing its index:
https://riptutorial.com/ 148
Chapter 32: Time
Syntax
• now()
• Dates.today()
• Dates.year(t)
• Dates.month(t)
• Dates.day(t)
• Dates.hour(t)
• Dates.minute(t)
• Dates.second(t)
• Dates.millisecond(t)
• Dates.format(t, s)
Examples
Current Time
To get the current date and time, use the now function:
julia> now()
2016-09-04T00:16:58.122
This is the local time, which includes the machine's configured time zone. To get the time in the
Coordinated Universal Time (UTC) time zone, use now(Dates.UTC):
julia> now(Dates.UTC)
2016-09-04T04:16:58.122
julia> Dates.today()
2016-10-30
The return value of now is a DateTime object. There are functions to get the individual components of
a DateTime:
julia> t = now()
2016-09-04T00:16:58.122
julia> Dates.year(t)
2016
julia> Dates.month(t)
9
https://riptutorial.com/ 149
julia> Dates.day(t)
4
julia> Dates.hour(t)
0
julia> Dates.minute(t)
16
julia> Dates.second(t)
58
julia> Dates.millisecond(t)
122
Since many of the Dates functions are exported from the Base.Dates module, it can save some
typing to write
using Base.Dates
which then enables accessing the qualified functions above without the Dates. qualification.
https://riptutorial.com/ 150
Chapter 33: Tuples
Syntax
• a,
• a, b
• a, b = xs
• ()
• (a,)
• (a, b)
• (a, b...)
• Tuple{T, U, V}
• NTuple{N, T}
• Tuple{T, U, Vararg{V}}
Remarks
Tuples have much better runtime performance than arrays for two reasons: their types are more
precise, and their immutability allows them to be allocated on the stack instead of the heap.
However, this more precise typing comes with both more compile-time overhead and more
difficulty achieving type stability.
Examples
Introduction to Tuples
Tuples are immutable ordered collections of arbitrary distinct objects, either of the same type or of
different types. Typically, tuples are constructed using the (x, y) syntax.
julia> tup[1]
1
julia> tup[2]
1.0
julia> tup[3]
"Hello, World!"
They implement the iterable interface, and can therefore be iterated over using for loops:
https://riptutorial.com/ 151
println(item)
end
1
1.0
Hello, World!
Tuples also support a variety of generic collections functions, such as reverse or length:
julia> reverse(tup)
("Hello, World!",1.0,1)
julia> length(tup)
3
Furthermore, tuples support a variety of higher-order collections operations, including any, all, map,
or broadcast:
julia> ()
()
julia> isempty(ans)
true
However, to construct a tuple of one element, a trailing comma is required. This is because the
parentheses (( and )) would otherwise be treated as grouping operations together instead of
constructing a tuple.
julia> (1)
1
julia> (1,)
(1,)
For consistency, a trailing comma is also allowed for tuples with more than one element.
https://riptutorial.com/ 152
Tuple types
Unlike other data types, Tuple types are covariant. Other data types in Julia are generally invariant.
Thus,
This is the case because everywhere a Tuple{Number, Number} is accepted, so too would a
Tuple{Int, Int}, since it also has two elements, both of which are numbers. That is not the case
for a Vector{Int} versus a Vector{Number}, as a function accepting a Vector{Number} may wish to
store a floating point (e.g. 1.0) or a complex number (e.g. 1+3im) in such a vector.
The covariance of tuple types means that Tuple{Number} (again unlike Vector{Number}) is actually an
abstract type:
julia> isleaftype(Tuple{Number})
false
julia> isleaftype(Vector{Number})
true
Tuple types may contain a terminating Vararg as their last parameter to indicate an indefinite
number of objects. For instance, Tuple{Vararg{Int}} is the type of all tuples containing any number
of Ints, possibly zero:
https://riptutorial.com/ 153
whereas Tuple{String, Vararg{Int}} accepts tuples consisting of a string, followed by any number
(possibly zero) of Ints.
Combined with co-variance, this means that Tuple{Vararg{Any}} describes any tuple. Indeed,
Tuple{Vararg{Any}} is just another way of saying Tuple:
Vararg accepts a second numeric type parameter indicating how many times exactly its first type
parameter should occur. (By default, if unspecified, this second type parameter is a typevar that
can take any value, which is why any number of Ints are accepted in the Varargs above.) Tuple
types ending in a specified Vararg will automatically be expanded to the requested number of
elements:
Notation exists for homogenous tuples with a specified Vararg: NTuple{N, T}. In this notation, N
denotes the number of elements in the tuple, and T denotes the type accepted. For instance,
julia> ans.types
svec(Int64,Int64,Int64,Int64,Int64,Int64,Int64,Int64,Int64,Int64)
Note that NTuples beyond a certain size are shown simply as NTuple{N, T}, instead of the expanded
Tuple form, but they are still the same type:
julia> Tuple{Int,Int,Int,Int,Int,Int,Int,Int,Int,Int}
NTuple{10,Int64}
Because Julia function parameter lists are themselves tuples, dispatching on various kinds of
tuples is often easier done through the method parameters themselves, often with liberal usage for
the "splatting" ... operator. For instance, consider the implementation of reverse for tuples, from
Base:
https://riptutorial.com/ 154
revargs() = ()
revargs(x, r...) = (revargs(r...)..., x)
reverse(t::Tuple) = revargs(t...)
Implementing methods on tuples this way preserves type stability, which is crucial for
performance. We can see that there is no overhead to this approach using the @code_warntype
macro:
Body:
begin
SSAValue(1) = (Core.getfield)(t::Tuple{Int64,Int64,Int64},2)::Int64
SSAValue(2) = (Core.getfield)(t::Tuple{Int64,Int64,Int64},3)::Int64
return
(Core.tuple)(SSAValue(2),SSAValue(1),(Core.getfield)(t::Tuple{Int64,Int64,Int64},1)::Int64)::Tuple{Int6
end::Tuple{Int64,Int64,Int64}
Although somewhat hard to read, the code here is simply getting creating a new tuple with values
3rd, 2nd, and 1st elements of the original tuple, respectively. On many machines, this compiles
down to extremely efficient LLVM code, which consists of loads and stores.
Tuples are frequently used for multiple return values. Much of the standard library, including two of
the functions of the iterable interface (next and done), returns tuples containing two related but
distinct values.
The parentheses around tuples can be omitted in certain situations, making multiple return values
easier to implement. For instance, we can create a function to return both positive and negative
https://riptutorial.com/ 155
square roots of a real number:
julia> pmsqrt(4)
(2.0,-2.0)
Destructuring assignment can be used to unpack the multiple return values. To store the square
roots in variables a and b, it suffices to write:
julia> a, b = pmsqrt(9.0)
(3.0,-3.0)
julia> a
3.0
julia> b
-3.0
Another example of this is the divrem and fldmod functions, which do an integer (truncating or
floored, respectively) division and remainder operation at the same time:
julia> q, r = divrem(10, 3)
(3,1)
julia> q
3
julia> r
1
https://riptutorial.com/ 156
Chapter 34: Type Stability
Introduction
Type instability occurs when a variable's type can change at runtime, and hence cannot be
inferred at compile-time. Type instability often causes performance problems, so being able to
write and identify type-stable code is important.
Examples
Write type-stable code
function sumofsins1(n::Integer)
r = 0
for i in 1:n
r += sin(3.4)
end
return r
end
function sumofsins2(n::Integer)
r = 0.0
for i in 1:n
r += sin(3.4)
end
return r
end
Timing the above two functions shows major differences in terms of time and memory allocations.
This is because of type-unstable code in sumofsins1 where the type of r needs to be checked for
every iteration.
https://riptutorial.com/ 157
Chapter 35: Types
Syntax
• immutable MyType; field; field; end
• type MyType; field; field; end
Remarks
Types are key to Julia's performance. An important idea for performance is type stability, which
occurs when the type a function returns only depends on the types, not the values, of its
arguments.
Examples
Dispatching on Types
On Julia, you can define more than one method for each function. Suppose we define three
methods of the same function:
foo(x) = 1
foo(x::Number) = 2
foo(x::Int) = 3
When deciding what method to use (called dispatch), Julia chooses the more specific method that
matches the types of the arguments:
julia> foo('one')
1
julia> foo(1.0)
2
julia> foo(1)
3
This facilitates polymorphism. For instance, we can easily create a linked list by defining two
immutable types, named Nil and Cons. These names are traditionally used to describe an empty
list and a non-empty list, respectively.
abstract LinkedList
immutable Nil <: LinkedList end
immutable Cons <: LinkedList
first
rest::LinkedList
end
https://riptutorial.com/ 158
We will represent the empty list by Nil() and any other lists by Cons(first, rest), where first is
the first element of the linked list and rest is the linked list consisting of all remaining elements. For
example, the list [1, 2, 3] will be represented as
julia> methods(isempty)
# 29 methods for generic function "isempty":
isempty(v::SimpleVector) at essentials.jl:180
isempty(m::Base.MethodList) at reflection.jl:394
...
We can simply use the function dispatch syntax, and define two additional methods of isempty.
Since this function is from the Base module, we have to qualify it as Base.isempty in order to extend
it.
Base.isempty(::Nil) = true
Base.isempty(::Cons) = false
Here, we did not need the argument values at all to determine whether the list is empty. Merely the
type alone suffices to compute that information. Julia allows us to omit the names of arguments,
keeping only their type annotation, if we need not use their values.
julia> methods(isempty)
# 31 methods for generic function "isempty":
isempty(v::SimpleVector) at essentials.jl:180
isempty(m::Base.MethodList) at reflection.jl:394
Clearly, determining whether a linked list is empty or not is a trivial example. But it leads up to
something more interesting:
https://riptutorial.com/ 159
How long is the list?
The length function from the standard library gives us the length of a collection or certain iterables.
There are many ways to implement length for a linked list. In particular, using a while loop is likely
fastest and most memory-efficient in Julia. But premature optimization is to be avoided, so let's
suppose for a second that our linked list need not be efficient. What's the simplest way to write a
length function?
Base.length(::Nil) = 0
Base.length(xs::Cons) = 1 + length(xs.rest)
The first definition is straightforward: an empty list has length 0. The second definition is also easy
to read: to count the length of a list, we count the first element, then count the length of the rest of
the list. We can test this method similarly to how we tested isempty:
Next steps
This toy example is pretty far from implementing all of the functionality that would be desired in a
linked list. It is missing, for instance, the iteration interface. However, it illustrates how dispatch can
be used to write short and clear code.
Immutable Types
The simplest composite type is an immutable type. Instances of immutable types, like tuples, are
values. Their fields cannot be changed after they are created. In many ways, an immutable type is
like a Tuple with names for the type itself and for each field.
Singleton types
Composite types, by definition, contain a number of simpler types. In Julia, this number can be
zero; that is, an immutable type is allowed to contain no fields. This is comparable to the empty
tuple ().
Why might this be useful? Such immutable types are known as "singleton types", as only one
instance of them could ever exist. The values of such types are known as "singleton values". The
standard library Base contains many such singleton types. Here is a brief list:
https://riptutorial.com/ 160
• Void,
the type of nothing. We can verify that Void.instance (which is special syntax for
retrieving the singleton value of a singleton type) is indeed nothing.
• Any media type, such as MIME"text/plain", is a singleton type with a single instance,
MIME("text/plain").
• The Irrational{:π}, Irrational{:e}, Irrational{:φ}, and similar types are singleton types, and
their singleton instances are the irrational values π = 3.1415926535897..., etc.
• The iterator size traits Base.HasLength, Base.HasShape, Base.IsInfinite, and Base.SizeUnknown
are all singleton types.
0.5.0
• In version 0.5 and later, each function is a singleton instance of a singleton type! Like any
other singleton value, we can recover the function sin, for example, from
typeof(sin).instance.
Because they contain nothing, singleton types are incredibly lightweight, and they can frequently
be optimized away by the compiler to have no runtime overhead. Thus, they are perfect for traits,
special tag values, and for things like functions that one would like to specialize on.
julia> MySingleton.instance
MySingleton()
Wrapper types
If zero-field immutable types are interesting and useful, then perhaps one-field immutable types
are even more useful. Such types are commonly called "wrapper types" because they wrap some
underlying data, providing an alternative interface to said data. An example of a wrapper type in
Base is String. We will define a similar type to String, named MyString. This type will be backed by
a vector (one-dimensional array) of bytes (UInt8).
https://riptutorial.com/ 161
immutable MyString <: AbstractString
data::Vector{UInt8}
end
Now our MyString type is ready for use! We can feed it some raw UTF-8 data, and it displays as we
like it to:
julia> MyString([0x48,0x65,0x6c,0x6c,0x6f,0x2c,0x20,0x57,0x6f,0x72,0x6c,0x64,0x21])
MyString: Hello, World!
Obviously, this string type needs a lot of work before it becomes as usable as the Base.String type.
immutable MyRational{T}
num::T
den::T
MyRational(n, d) = (g = gcd(n, d); new(n÷g, d÷g))
end
MyRational{T}(n::T, d::T) = MyRational{T}(n, d)
julia> MyRational(10, 6)
MyRational{Int64}(5,3)
https://riptutorial.com/ 162
Chapter 36: Unit Testing
Syntax
• @test [expr]
• @test_throws [Exception] [expr]
• @testset "[name]" begin; [tests]; end
• Pkg.test([package])
Remarks
The standard library documentation for Base.Test covers additional material beyond that shown in
these examples.
Examples
Testing a Package
To run the unit tests for a package, use the Pkg.test function. For a package named MyPackage, the
command would be
julia> Pkg.test("MyPackage")
though obviously, one cannot expect it to match the above exactly, since different packages use
different frameworks.
https://riptutorial.com/ 163
julia> Pkg.test()
Unit tests are declared in the test/runtests.jl file in a package. Typically, this file begins
using MyModule
using Base.Test
The basic unit of testing is the @test macro. This macro is like an assertion of sorts. Any boolean
expression can be tested in the @test macro:
@test 1 + 1 == 2
@test iseven(10)
@test 9 < 10 || 10 < 9
julia> @test 1 + 1 == 2
Test Passed
Expression: 1 + 1 == 2
Evaluated: 2 == 2
julia> @test 1 + 1 == 3
Test Failed
Expression: 1 + 1 == 3
Evaluated: 2 == 3
ERROR: There was an error during testing
in record(::Base.Test.FallbackTestSet, ::Base.Test.Fail) at ./test.jl:397
in do_test(::Base.Test.Returned, ::Expr) at ./test.jl:281
The test macro can be used in just about anywhere, such as in loops or functions:
0.5.0
https://riptutorial.com/ 164
In version v0.5, test sets are built into the standard library Base.Test module, and you don't have to
do anything special (besides using Base.Test) to use them.
0.4.0
Test sets are not part of Julia v0.4's Base.Test library. Instead, you have to REQUIRE the BaseTestNext
module, and add using BaseTestNext to your file. To support both version 0.4 and 0.5, you could
use
if VERSION ≥ v"0.5.0-dev+7720"
using Base.Test
else
using BaseTestNext
const Test = BaseTestNext
end
It is helpful to group related @tests together in a test set. In addition to clearer test organization,
test sets offer better output and more customizability.
To define a test set, simply wrap any number of @tests with a @testset block:
Even if a test set contains a failing test, the entire test set will be run to completion, and the
failures will be recorded and reported:
-: Test Failed
Expression: 2 - 2 == 1
https://riptutorial.com/ 165
Evaluated: 0 == 1
in record(::Base.Test.DefaultTestSet, ::Base.Test.Fail) at ./test.jl:428
...
-: Error During Test
Test threw an exception of type MethodError
Expression: 3 - () == 3
MethodError: no method matching -(::Int64, ::Tuple{})
...
Test Summary: | Pass Fail Error Total
- | 2 1 1 4
ERROR: Some tests did not pass: 2 passed, 1 failed, 1 errored, 0 broken.
...
If the tests pass, then this will only show the results for the outermost test set:
But if the tests fail, then a drill-down into the exact test set and test causing the failure is reported.
The @testset macro can be used with a for loop to create many test sets at once:
which reports
A common structure is to have outer test sets test components or types. Within these outer test
sets, inner test sets test behaviour. For instance, suppose we created a type UniversalSet with a
https://riptutorial.com/ 166
singleton instance that contains everything. Before we even implement the type, we can use test-
driven development principles and implement the tests:
We can then start implementing our functionality until it passes our tests. The first step is to define
the type:
Only two of our tests pass right now. We can implement in:
This also makes some of our subset tests to pass. However, the issubset (⊆) fallback doesn't work
for UniversalSet, because the fallback tries to iterate over elements, which we can't do. We can
simply define a specialization that makes issubset return true for any set:
Testing Exceptions
Exceptions encountered while running a test will fail the test, and if the test is not in a test set,
terminate the test engine. Usually, this is a good thing, because in most situations exceptions are
not the desired result. But sometimes, one wants to test specifically that a certain exception is
raised. The @test_throws macro facilitates this.
https://riptutorial.com/ 167
julia> @test_throws BoundsError [1, 2, 3][4]
Test Passed
Expression: ([1,2,3])[4]
Thrown: BoundsError
The error is caused by the fact that none of 0.1, 0.2, and 0.3 are represented in the computer as
exactly those values — 1//10, 2//10, and 3//10. Instead, they are approximated by values that are
very close. But as seen in the test failure above, when adding two approximations together, the
result can be a slightly worse approximation than is possible. There is much more to this subject
that cannot be covered here.
But we aren't out of luck! To test that the combination of rounding to a floating point number and
floating point arithmetic is approximately correct, even if not exact, we can use the isapprox
function (which corresponds to operator ≈). So we can rewrite our test as
https://riptutorial.com/ 168
Of course, if our code was entirely wrong, the test will still catch that:
The isapprox function uses heuristics based off the size of the numbers and the precision of the
floating point type to determine the amount of error to be tolerated. It's not appropriate for all
situations, but it works in most, and saves a lot of effort implementing one's own version of
isapprox.
https://riptutorial.com/ 169
Chapter 37: while Loops
Syntax
• while cond; body; end
• break
• continue
Remarks
The while loop does not have a value; although it can be used in expression position, its type is
Void and the value obtained will be nothing.
Examples
Collatz sequence
The while loop runs its body as long as the condition holds. For instance, the following code
computes and prints the Collatz sequence from a given number:
function collatz(n)
while n ≠ 1
println(n)
n = iseven(n) ? n ÷ 2 : 3n + 1
end
println("1... and 4, 2, 1, 4, 2, 1 and so on")
end
Usage:
julia> collatz(10)
10
5
16
8
4
2
1... and 4, 2, 1, 4, 2, 1 and so on
It is possible to write any loop recursively, and for complex while loops, sometimes the recursive
variant is more clear. However, in Julia, loops have some distinct advantages over recursion:
• Julia does not guarantee tail call elimination, so recursion uses additional memory and may
cause stack overflow errors.
• And further, for the same reason, a loop can have decreased overhead and run faster.
https://riptutorial.com/ 170
Sometimes, one wants to run some initialization code once before testing a condition. In certain
other languages, this kind of loop has special do-while syntax. However, this syntax can be
replaced with a regular while loop and break statement, so Julia does not have specialized do-while
syntax. Instead, one writes:
local name
Note that in some situations, such loops could be more clear with recursion:
function getname()
println("Type your name, without lowercase letters:")
name = readline()
if any(islower, name)
getname() # this name is unacceptable; try again
else
name # this name is good, return it
end
end
Breadth-first search
0.5.0
(Although this example is written using syntax introduced in version v0.5, it can work with few
modifications on older versions also.)
This implementation of breadth-first search (BFS) on a graph represented with adjacency lists
uses while loops and the return statement. The task we will solve is as follows: we have a
sequence of people, and a sequence of friendships (friendships are mutual). We want to
determine the degree of the connection between two people. That is, if two people are friends, we
will return 1; if one is a friend of a friend of the other, we will return 2, and so on.
First, let’s assume we already have an adjacency list: a Dict mapping T to Array{T, 1}, where the
keys are people and the values are all the friends of that person. Here we can represent people
with whatever type T we choose; in this example, we will use Symbol. In the BFS algorithm, we
keep a queue of people to “visit”, and mark their distance from the origin node.
# until the queue is empty, get elements and inspect their neighbours
https://riptutorial.com/ 171
while !isempty(queue)
# shift the first element off the queue
current = shift!(queue)
Now, we will write a function to build an adjacency list given a sequence of people, and a
sequence of (person, person) tuples:
result
end
https://riptutorial.com/ 172
Jean is connected to himself in 0 steps:
Jean and Gavroche are connected indirectly through Cosette and then Marius, so their degree is 3
:
Javert and Marius are not connected through any chain, so an error is raised:
https://riptutorial.com/ 173
Credits
S.
Chapters Contributors
No
Getting started with Andrew Piliser, becko, Community, Dawny33, Fengyang Wang,
1
Julia Language Kevin Montrose, prcastro
Cross-Version
10 Fengyang Wang
Compatibility
Higher-Order
16 Fengyang Wang, mnoronha
Functions
https://riptutorial.com/ 174
21 Modules Fengyang Wang
Reading a
24 DataFrame from a Pranav Bhat
file
https://riptutorial.com/ 175