Julia is an open-source, multi-platform, high-level, high-performance programming language for technical computing.
Julia has an LLVMLow-Level Virtual Machine (LLVM) is a compiler infrastructure to build intermediate and/or binary machine code.-based JITJust-In-Time compilation occurs at run-time rather than prior to execution, which means it offers both the speed of compiled code and the flexibility of interpretation. The compiler parses the code and infers types, after which the LLVM code is generated, which in turn is compiled into native code. compiler that allows it to match the performance of languages such as C and FORTRAN without the hassle of low-level code. Because the code is compiled on the fly you can run (bits of) code in a shell or REPLRead-Eval-Print-Loop, which is part of the recommended workflow.
Julia is dynamically typed, provides multiple dispatchBecause function argument types are determined at run-time, the compiler can choose the implementation that is optimized for the provided arguments and the processor architecture., and is designed for parallelism and distributed computation.
Julia has a built-in package manager.
Julia has many built-in mathematical functions, including special functions (e.g. Airy, Bessel, Gamma), and supports complex numbers right out of the box.
Julia allows you to generate code automagically thanks to Lisp-inspired macros.
Julia was born in 2012.
Assignment |
|
Constant declaration | const DATE_OF_BIRTH = 2012 |
End-of-line comment | i = 1 # This is a comment |
Delimited comment | #= This is another comment =# |
Chaining |
|
Function definition |
|
Insert LaTeX symbols | \delta + [Tab] |
Basic arithmetic | + , - , * , / |
Exponentiation | 2^3 == 8 |
Fractional division | 3//12 == 0.25 |
Inverse division | 7\3 == 3/7 |
Remainder | x % y or rem(x,y) |
Negation | !true == false |
Equality | a == b |
Equality (composite typesThe default comparison operator compares only memory addresses for composite types.) | is(a, b) # done on bit level |
Inequality | a != b or a ≠ b |
Less and larger than | < and > |
Less than or equal to | <= or ≤ |
Greater than or equal to | >= or ≥ |
Element-wise operation |
|
Not a number | isnan(NaN) not(!) NaN == NaN |
Ternary operator | a == b ? "Equal" : "Not equal" |
Short-circuited AND and OR | a && b and a || b |
Recall last result | ans |
Interrupt execution | [Ctrl] + [C] |
Clear screen | [Ctrl] + [L] |
Clear all variables and reset session | workspace() |
Run program | include("filename.jl") |
Get help for func is defined |
?func |
See all places where func is defined |
apropos("func") |
Escape to OS command line | ; |
Exit | quit() or [Ctrl] + [D] |
Packages must be registered before they are visible to the package manager.
List installed packages (human-readable) | Pkg.status() |
List installed packages (machine-readable) | Pkg.installed() |
Location of package cache | Pkg.dir() |
Update all packages | Pkg.update() |
Install PackageName |
Pkg.add("PackageName") |
Pkg.build("PackageName") |
|
Use PackageName (after install) |
using PackageName |
Remove PackageName |
Pkg.rm("PackageName") |
Character | chr = 'C' |
String | str = "A string" |
Character code | Int('J') == 74 |
Character from code | Char(74) == 'J' |
Any UTF character |
|
Loop through characters |
|
Concatenation | str = "Learn" * " " * "Julia" |
String interpolation |
|
First matching character or regular expression | search("Julia", 'i') == 4 |
Replace substring or regular expression | replace("Julia", "a", "us") == "Julius" |
Last index (of collection) | endof("Hello") == 5 |
Number of characters | length("Hello") == 5 |
Regular expression | pattern = r"l[aeiou]" |
Subexpressions |
|
All occurrences | matchall(pat, str) |
All occurrences (as iterator) | eachmatch(pat, str) |
Beware of multi-byte Unicode encodings in UTF-8:
10 == endof("Ångström") != length("Ångström") == 8
Strings are immutable.
Integer types | IntN and UIntN , with N ∈
{8 ,16 ,32 ,64 ,128 }BigInt |
Floating-point types | FloatN with N ∈ {16 , 32 ,
64 }BigFloat |
Minimum and maximum values by type |
|
Complex types | ComplexN with N ∈ {64 ,
128 }
|
Imaginary unit | im |
Machine precision | eps() # same as eps(Float64) |
Rounding |
|
Random numbers |
|
Type conversions |
|
Julia does not automatically check for numerical overflow.
Global constants |
|
Identity matrix | eye(n) # n×n |
Define matrix | M = [1 0; 0 1] # eye(2) |
Matrix dimensions | size(M) |
Select i th row |
M[i, :] |
Select i th column |
M[:, i] |
Matrix transposition | transpose(M) |
Conjugate matrix transposition | M' or ctranspose(M) |
Matrix trace | trace(M) |
Matrix determinant | det(M) |
Matrix rank | rank(M) |
Matrix eigenvalues | eigvals(M) |
Matrix eigenvectors | eigvecs(M) |
Matrix inverse | inv(M) |
Solve M*x == v |
M\v is betterNumerically more stable and typically also faster.
than inv(M)*v
|
Moore-Penrose pseudo-inverse | pinv(M) |
Julia provides many mathematical (special) and statistical functions.
Julia has built-in support for matrix decompositions.
Conditional | if -elseif -else -end
|
Simple for loop |
|
Unnested for loop |
|
Enumeration |
|
while loop |
|
Exit loop | break |
Exit iteration | continue |
All arguments to functions are passed by reference.
Functions with !
appended change at least one argument, typically
the first: sort!(arr)
.
Required arguments are separated with a comma and use the positional notation.
Optional arguments need a default value in the signature, defined with
=
.
Keyword arguments use the named notation and are listed in the function's signature after the semicolon:
function func(req1, req2; key1=dflt1, key2=dflt2)
# do stuff
end
The semicolon is not required in the call to a function that accepts keyword arguments.
The return
statement is optional but highly recommended.
Multiple data structures can be returned as a tuple in a single
return
statement.
Command line arguments julia script.jl arg1 arg2...
can be processed
from global constant ARGS
:
for arg in ARGS
println(arg)
end
Anonymous functions can best be used in collection functions or list
comprehensions: x -> x^2
.
Functions can accept a variable number of arguments:
function func(a...)
println(a)
end
func(1, 2, [3:5]) # tuple: (1,2,[3,4,5])
Functions can be nested:
function outerfunction()
# do some outer stuff
function innerfunction()
# do inner stuff
# can access prior outer definitions
end
# do more outer stuff
end
Julia generates specialized versionsMultiple dispatch a type of polymorphism that dynamically determines which version of a function to call. In this context, dynamic means that it is resolved at run-time, whereas method overloading is resolved at compile time. Julia manages multiple dispatch completely in the background. Of course, you can provide custom function overloadings with type annotations. of functions based on data types. When a function is called with the same argument types again, Julia can look up the native machine code and skip the compilation process.
Stack overflow is possible when recursive functions nest many levels deep. Trampolining can be used to do tail-call optimization, as Julia does not do that automatically yet. Alternatively, you can rewrite the tail recursion as an iteration.
Declaration | arr = Float64[] |
Pre-allocation | sizehint(arr, 10^4) |
Access and assignment | arr[1] = "Some text" |
Comparison |
|
Copy elements (not address) |
|
Select subarray from m to n |
arr[n:m] |
n -element array with 0.0 s |
zeros(n) |
n -element array with 1.0 s |
ones(n) |
n -element array with #undef s |
cell(n) |
n equally spaced numbers from start to stop
|
linspace(start, stop, n) |
Array with n random Int8 elements |
rand(Int8, n) |
Fill array with val |
fill!(arr, val) |
Pop last element | pop!(arr) |
Pop first element | shift!(a) |
Push val as last element |
push!(arr, val) |
Push val as first element |
unshift!(arr, val) |
Remove element at index idx |
splice!(arr, idx) |
Sort | sort!(arr) |
Append a with b |
append!(a,b) |
Check whether val is element |
in(val, arr) or val in arr |
Scalar product | dot(a, b) == sum(a .* w) |
Change dimensions (if possible) |
|
To string (with delimiter del between elements) |
join(arr, del) |
Dictionary |
|
All keys (iterator) | keys(d) |
All values (iterator) | values(d) |
Loop through key-value pairs |
|
Check for key :k |
haskey(d, :k) |
Copy keys (or values) to array |
|
Dictionaries are mutable; when symbols are used as keys, the keys are immutable.
Declaration | s = Set([1, 2, 3, "Some text"]) |
Union s1 ∪ s2 |
union(s1, s2) |
Intersection s1 ∩ s2 |
intersect(s1, s2) |
Difference s1 \ s2 |
setdiff(s1, s2) |
Difference s1 △ s2 |
symdiff(s1, s2) |
Subset s1 ⊆ s2 |
issubset(s1, s2) |
Checking whether an element is contained in a set is done in O(1).
Apply f to all elements of collection coll
|
map(f, coll) or
|
Filter coll for true values of f |
filter(f, coll) |
List comprehension | arr = [f(elem) for elem in coll] |
Julia has no classes and thus no class-specific methods.
Types mimic classes in a simple way.
Abstract types can be subtyped but not instantiated.
Concrete types cannot be subtyped.
Immutable types enhance performance and are thread safe, as they can be shared among threads without the need for synchronization.
Type annotation | var::TypeName |
Type declaration |
|
ImmutableImmutable types are passed around by value (i.e. copying), whereas mutable types are passed by reference. type declaration | replace type with immutable |
Type alias | typealias Nerd Programmer |
Type instantiation |
|
Subtype declaration |
|
Parametric type |
|
Traverse type hierarchy | super(TypeName) and subtypes(TypeName) |
Default supertype | Any |
All fields | names(TypeName) |
All field types | TypeName.types |
Nullable type | Nullable{T} |
When a type is defined with an inner constructor, the default
outer constructors are not available and have to be defined manually if
need be. An inner constructor is
best used to check whether the parameters conform to certain (invariance)
conditions. Obviously, these invariants can be violated by accessing and
modifying the fields directly, unless the
type is defined as immutable. The new
keyword may be used to create
an object of the same type.
Type parameters are invariant, which means that Point{Float64} <:
Point{Real}
is false, even though Float64 <: Real
. Tuple
types, on the other hand, are
covariant: Tuple{Float64} <: Tuple{Real}
.
The type-inferred form of Julia's internal representation can be found with
code_typed()
. This is useful to identify where Any
rather than type-specific native
code is generated.
Throw SomeExcep |
throw(SomeExcep()) |
Rethrow current exception | rethrow() |
Define NewExcep |
|
Throw error with msg text |
error(msg) |
Throw warning with msg text |
warn(msg) |
Throw information with msg text |
info(msg) |
Handler |
|
Modules are separate global variable workspaces that group together similar functionality.
Definition |
|
Include filename.jl |
include("filename.jl") |
Load |
|
The main difference between using
and import
is that
the former automatically resolves names, so you can write x
in your
code, whereas the latter
requires the namespace too: ModuleName.x
. Moreover,
using
only loads exported names by default while
import
loads all names defined in the module.
Julia is homoiconic: programs are represented as data structures of the language
itself. In fact, everything is an expression Expr
.
Symbols are interned stringsOnly one copy of each distinct (immutable) string value is stored. prefixed with a colon. Symbols are more efficient and they are typically used as identifiers, keys (in dictionaries), or columns in data frames. Symbols cannot be concatenated.
Quoting :( ... )
or quote ... end
creates an
expression, just like parse(str)
This form is probably most familiar
to people with knowledge of dynamic SQL. The parse
function is similar to Oracle's and PostgreSQL's EXECUTE IMMEDIATE
statement or SQL Server's
sp_executesql()
procedure., and Expr(:call, ...)
.
x = 1
line = "1 + $x" # some code
expr = parse(line) # make an Expr object
typeof(expr) == Expr # true
dump(expr) # generate abstract syntax tree
eval(expr) == 2 # evaluate Expr object: true
Macros allow generated code (i.e. expressions) to be included in a program.
Definition |
|
Usage | @macroname(ex1, ex2, ...) or @macroname(ex1, ex2,
...) |
Built-in macros |
|
Rules for creating hygienic macros:
local
.eval
inside macro.$(esc(expr))
.
Tasks provide a lightweight threading mechanism and they can be suspended and resumed at will. Tasks follow the producer-consumer model, which means that between consumption calls the task is suspended.
Tasks are not executed in different threads, so they cannot run on different processors.
Tasks have low overhead because switching between them does not consume stack space unlike normal function calls.
Define | t = Task( () -> somefunc(...) ) or t = @task
somefunc(...) |
Produce |
|
Consume | consume(t) |
Launch REPL with N workers |
julia -p N |
Number of available workers | nprocs() |
Add N workers |
addprocs(N) |
See all worker ids |
|
Remove worker | rmprocs(pid) |
Run f with arguments args on pid
|
|
Run f with arguments args on pid
(more efficient)
|
|
Run f with arguments args on any worker |
r = @spawn f(args) ... fetch(r) |
Run f with arguments args on all workers |
r = [@spawnat w f(args) for w in workers()] ... fetch(r)
|
Make expr available to all workers |
@everywhere expr |
Parallel for loop with reducerA reducer combines the results from different (independent) workers.
function red
|
|
Apply f to all elements in collection coll
|
pmap(f, coll) |
Workers are also known as concurrent/parallel processes.
Modules with parallel processing capabilities are best split into a functions file that contains all the functions and variables needed by all workers, and a driver file that handles the processing of data. The driver file obviously has to import the functions file.
A non-trivial (word count) example of a reducer function is provided by Adam DeConinck.
Read stream |
|
Read file |
|
Read CSV file | readdlm(filename, ',' header=true) or readcsv(filename)
|
Read (gzipped) CSV | df = readtable(filename) |
DescribeSimilar to summary(df) in R.
data frame
|
describe(df) |
Write CSV | writetable("filename.csv", df) |
Make vector of column col |
v = df[:col] |
Sort by col |
sort!(df, cols = [:col]) |
PoolSimilar to df$col = as.factor(df$col) in R.
col
|
pool!(df, [:col]) |
List col levels |
levels(df[:col]) |
All observations with col==val |
|
Reshape from wide to long format |
|
Reshape from long to wide format |
|
Type | typeof(name) |
Type check | isa(name, TypeName) |
List subtypes | subtypes(name) |
List supertype | super(TypeName) |
Function methods | methods(func) |
JIT bytecode | code_llvm(expr) |
Assembly code | code_native(expr) |
Dates and times | Dates |
ODBC drivers | ODBC |
JSON parser | JSON |
Test framework | Test |
Static code checking | Lint |
Statically sized arrays | ImmutableArrays |
Distributed arrays | DistributedArrays |
Arrays with NA |
DataArrays |
Data frames | DataFrames |
Statistical distributions | Distributions |
Bootstrapping | Bootstrap |
Evolutionary algorithms | Evolutionary |
Data visualization à la ggplot2 |
Gadfly |
Analytics |
|
Statistics | JuliaStats |
Automatic differentiation | JuliaDiff |
Numerical optimization | JuliaOpt |
Web | JuliaWeb |
A nice example of analytics with Julia is the Kaggle's Titanic competition.
The main convention in Julia is to avoid underscores unless they are required for legibility.
Variable names are in lower (or snake) case: somevariable
.
Constants are in upper case: SOMECONSTANT
.
Functions are in lower (or snake) case: somefunction
.
Macros are in lower (or snake) case: @somemacro
.
Type names are in initial-capital camel case: SomeType
.
Julia files have the jl
extension.
John Myles White has provided a comprehensive style guide with more details and suggestions.
sizehint
for large arrays.arr = nothing
.disable_gc()
.
...
) operator for keyword arguments.!
to avoid copying data
structures.
try
-catch
in (computation-intensive) loops.
Any
in collections.for
loops
unlike R, MATLAB or Python.
eval
at run-time.