Variables, Control Structures & Packages

Variables and elementary types

Defining variables in Julia works like in most languages:

In [ ]:
int = 4      # An integer
In [ ]:
str = "Hi"   # A string
In [ ]:
float = 1.2  # A floating-point number
In [ ]:
bool = true  # A boolean (also false)

The type is automatically inferred:

In [ ]:
typeof(int)
In [ ]:
typeof(str)
In [ ]:
typeof(float)

Julia supports a large range of integer and floating-point types out of the box, for example:

In [ ]:
x = UInt8(1)              # 8-bit wide unsigned integer
y = Int32(-1)             # 32-bit wide signed integer
z = Float32(0.2)          # single precision
α = Float16(-1.0)         # half precision
β = ComplexF64(2. + 8im)  # Complex number (composed of two Float64)

Operations between these types may involve automatic promotion.

In [ ]:
println(typeof(1 + 1.2))
println(typeof(x * y))  # x was Uint8, y was Int32
In [ ]:
x = Float32(1.)
4x                     # No "*" needed
In [ ]:
println(5 % 2)
In [ ]:
5^6
In [ ]:
2.3^6
In [ ]:
println(4 - Float32(2))
println(8 / 3)  # Note conversion to Float64
typeof(8 / 3)

This can be avoided using a rational:

In [ ]:
x = 6 // 3
println(typeof(x), " ", Int(x))
In [ ]:
2  # == sqrt(2)

Notice the * operation for string concatenation ...

In [ ]:
"Concatenating " * "strings is done " * "using multiplication"

Type conversion is possible, for example using constructors:

In [ ]:
Int(2.0)
In [ ]:
x = Float32(1.0)
Float64(x)
In [ ]:
Int(2.3)
In [ ]:
UInt8(-1)
In [ ]:
Float64(2.0 + 1im)

Sometimes a special conversion function or syntax exists:

In [ ]:
string(1.2)
In [ ]:
ceil(Int, 2.3)
In [ ]:
round(Int32, 4.5)
In [ ]:
x = 1.2
println("x is currently $x.")
println("two less is $(x - 2)")

Arbitrary precision is supported out of the box:

In [ ]:
# arbitrary precision
b = big"12"; println(b, " is a ", typeof(b))
f = big"-1.2"; println(f, " is a ", typeof(f))
In [ ]:
# Or wir an explicit cast:
b = BigInt(12)
f = BigFloat(-1.2)  # Note the difference to the above result

Julia is garbage-collected, meaning that memory occupied by a variable value will be freed automatically if no longer needed, e.g.:

In [ ]:
x = 15
println(x)
x = "abc";    # x newly assigned, memory by the "15" will be freed at some point in the future

Exercise

Compute $2^{100}$:

  • As fast as possible
  • As exact as possible

Basic control structures

Unsurprisingly Julia has the standard for and while loops and the if conditionals.

In [ ]:
x = 3; y = 5

if x < y
    println("x is less than y")
elseif x > y
    println("x is greater than y")
else
    println("x is equal to y")
end
In [ ]:
x = 5
while x > 3
    println("Now x is $x")
    x -= 1
end
In [ ]:
for i in 1:5
    println("Hello from number $i")
end

Where the 1:5 is a Range-object, but it could be any iterable. There are a few syntax variations, including:

In [ ]:
accu = 0
for j  1:0.5:3  # Note the step parameter
    accu += j
end
println(accu)

Worth mentioning are also ||, && and ?:

In [ ]:
true  || println("The RHS of || is only run")
false || println("if the LHS is false")
In [ ]:
iseven(3) && println("The RHS of || is only run")
isodd(3)  && println("if the LHS is true")
In [ ]:
x = 3
x < 5 ? "smaller than 5" : "larger or equal 5"

Generally expressions can be made one block using begin and end or ( and ). The last statement determines the returned value.

In [ ]:
z = begin
    x = 5
    y = -3
    x + y
end
In [ ]:
# Or equivalently
z = (x = 5; y = -3; x + y)

Exercise

Compute $$ 15! \qquad 100! \qquad \left(\begin{array}{c} 100 \\ 15 \end{array}\right) $$ with the Julia you know so far.

Julia packages

The Julia standard library is already surprisingly rich in functionality. Features are grouped into smaller packages according to topics (e.g. LinearAlgebra, SparseArrays, Test, ...). The most basic part of the standard library resides in the special package Base and is always automatically to the programmer. If anything beyond Base is needed, that is both packages of the standard library or any other third-party package, this needs to be explicitly installed and imported before use.

For example. The randn function produces a list or array of random numbers. It is already available in Base and we can thus directly use it to create 4 random numbers:

In [ ]:
d = randn(4)

The returned object in d is an Array, a type from Base, which we will discuss later. We wish to create a diagonal Matrix with these four vaules on the diagonal. For this we need the LinearAlgebra.Diagonal datastructure. We make LinearAlgebra available to our scope with a using statement:

In [ ]:
using LinearAlgebra

This (might) cause an error, because LinearAlgebra is not yet installed. We resolve this as recommended:

In [ ]:
import Pkg; Pkg.add("LinearAlgebra")

Note, that import is roughly the same as using except the package is not brought into scope. We will neglect this detail for this course and just use using for all packages we use.

Having installed the package, we are able to use LinearAlgebra and make the diagonal matrix:

In [ ]:
using LinearAlgebra
Diagonal(d)

The Julia REPL

Apart from interacting with Julia via a webbrowser and Jupyter (as we will do it in this course). There is also the alternative option to use the Julia REPL. You can get to the REPL by starting julia from a terminal. The result is a prompt similar to

               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.3.0 (2019-11-26)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

julia>

In this prompt you can directly start typing julia expressions, which will be executed upon [Enter]. The REPL is very nice to prototype things and with some optional customisations becomes a very powerful tool. We will not use the REPL much in this course, but still I want to mention some basic aspects to get you started. Mostly this is for people who prefer the commandline over graphics (like me):

  • Exiting the shell can be achieved using exit() or Ctrl + D.
  • Inside the shell tab completion is available.
  • The REPL comes in multiple modes. The most useful ones are the (default) Julian mode, Pkg mode (for installing packages), shell mode (for running one-shot shell commands) and help mode. You can get from Julian mode to another by typing a magic key, see the list below. You get back to Julian mode by [Backspace] on an empty prompt.
Magic key Prompt Mode
[Backspace] julia> Julian mode (Command mode)
] (v1.3) pkg> Pkg mode
? help> Help mode
; shell> Shell command mode
  • To understand Pkg mode better, type help once the (v1.3) pkg> prompt shows.
  • In each mode Ctrl + R allows to search backwards in history for a previously entered command.

Exercise

External packages are installed exactly the same as standard library packages. Their data is automatically fetched from the General as needed.

Make sure you have an internet connection. From the REPL or from the notebook, install the following packages as we will need them later:

  • Plots
  • Zygote
  • BenchmarkTools