01 JULIA1 - 1A: Introduction and setup of the environment (8:37)
01 JULIA1 - 1B: Comments, code organisation, Unicode support, broadcasting (12:4)
01 JULIA1 - 1C: Math operators, quotation marks (6:49)
01 JULIA1 - 1D: Missing values (10:4)
01 JULIA1 - 1E: Stochasticity in programming (9:14)
0101 - Basic Syntax Elements
Some stuff to set-up the environment..
julia> cd(@__DIR__)
julia> using Pkg
julia> Pkg.activate(".")
Activating project at `~/work/SPMLJ/SPMLJ/buildedDoc/01_-_JULIA1_-_Basic_Julia_programming`
julia> ENV["PYTHON"] = "" # This will be needed in a further segment
""
julia> ENV["R_HOME"] = "*" # This will be needed in a further segment # If using a Julia version different than 1.10 please uncomment and run the following line (reproducibility guarantee will however be lost) # Pkg.resolve()
"*"
julia> Pkg.instantiate()
julia> using Random
julia> Random.seed!(123)
Random.TaskLocalRNG()
Comments
Similar to many other languages, everything that folows a hash symbol (#
), up to the end of the line, is considered by Julia as a comment. Julia supports also multi-line, middle-of-the-line and nested comments using the #= ...comment... =#
syntax:
julia> # This is a comment a = 1 # also this one
1
julia> a = #= also this one =# 1 #= also #= this =# one =#
1
Code organisation
Delimiting the end of a programming statement with a semi-colon, is optional, and has the effect to block the normal display on the terminal of the output of that statement.
julia> # Semicolon: a = 1
1
julia> a = 1;
All code blocks (like the foor loop that we'll study in detail in the Control flow and functions segment) ends with the end
keyword, like in the example below:
julia> for i in 1:3 println("i is $i") end # Keyword `end` to finish a block
i is 1 i is 2 i is 3
Function calls require to specify the name of the function to call followed straight away (without spaces) with the function arguments inside round brackets:
julia> println("Hello world!") ##println ("This would error!")
Hello world!
Unicode support
We can can use any fancy Unicode symbol, including modifiers, in the names of variables, types, functions..
julia> using Statistics # for the `mean` function, in the Standard Library
julia> σ²(x) = sum( (x .- mean(x)).^2 )/length(x) # The superscript `²` is just another character, it has no syntactic value.
σ² (generic function with 1 method)
julia> σ²([1,2,3])
0.6666666666666666
julia> x̄ₙ = 10
10
julia> vàlidVarNαme! = 2
2
julia> 🥞 = 8
8
julia> 🍴(🥫,parts=4) = 🥫/parts
🍴 (generic function with 2 methods)
julia> 🍰 = 🍴(🥞)
2.0
Broadcasting
Broadcasting refers to the capacity to dispatch a function that accepts a scalar argument to a whole collection. Broadcasting will then call the function with each individual element of the collection. It is implemented by postfixing a function name (or prefixing an operator) with a single dot at call time:
julia> 10 .+ [1,2,3]
3-element Vector{Int64}: 11 12 13
julia> add2(x) = x + 2
add2 (generic function with 1 method)
julia> add2(10) # add2([1,2,3]) # would return an error
12
julia> add2.([1,2,3]) # any, including user defined functions, can be broadcasted. No need for map, for loops, etc..
3-element Vector{Int64}: 3 4 5
julia> add2.((1,2,3)) # not only for arrays, here is a Tuple
(3, 4, 5)
julia> add2.(Set([1,2,3,2])) # and here a Set
3-element Vector{Int64}: 4 5 3
julia> .+([1,2,3],[10,20,30]) # fine here # .+([1,2,3],[10,20]) # DimensionMismatch error: the input of the the broadcasted arguments must have the same size or be a scalar
3-element Vector{Int64}: 11 22 33
To "protect" one specific argument to be broadcasted, use Ref(arg)
:
julia> foo(x,y::AbstractArray) = [yi+x for yi in y] # a function that wants the first argument as scalar and the second as vector
foo (generic function with 1 method)
julia> foo(1,[10,20]) # foo.([1,2],[10,20]) # error, as we try to broadcast also the seond element, but we can't call `foo` with two integers
2-element Vector{Int64}: 11 21
julia> foo.([1,2],Ref([10,20])) # now it is fine, we broadcast only the first argument
2-element Vector{Vector{Int64}}: [11, 21] [12, 22]
1 based arrays
Arrays start indexing at 1 (like R, Fortran, Matlab,...) instead of 0 (like in C, Python, Java...)
julia> a = [1,2,3]
3-element Vector{Int64}: 1 2 3
julia> a[1]
1
julia> collect(1:3) # in ranges, both extremes are included
3-element Vector{Int64}: 1 2 3
Should array indices start at 0 or 1? My compromise of 0.5 was rejected without, I thought, proper consideration. –Stan Kelly-Bootle
Basic Mathematic operations
All standard mathematical arithmetic operators (+
,-
,*
,/
) are supported in the obvious way:
julia> a = 2^4 # rise to power
16
julia> b = ℯ^2; #= or =# b = exp(2) # Exponential with base ℯ
7.38905609893065
julia> d = log(7.3890) # base ℯ
1.9999924078065106
julia> e = log(10,100) # custom base
2.0
julia> f = 5 ÷ 2 # integer division
2
julia> e = 5 % 2 # reminder (modulo operator)
1
julia> a = 2//3 + 1//3 # rational numbers
1//1
julia> typeof(a)
Rational{Int64}
julia> π == pi # some irrational constants
true
julia> typeof(ℯ)
Irrational{:ℯ}
julia> convert(Float64,a)
1.0
Quotation
julia> a = 'k' # single quotation mark: a single Char
'k': ASCII/Unicode U+006B (category Ll: Letter, lowercase)
julia> b = "k" # double quotation mark: a (Unicode) String #c = 'hello' # error !
"k"
julia> c = "hello"
"hello"
julia> d = `echo hello` # backtick: define a command to (later) run (e.g. on the OS)
`echo hello`
julia> e = """a multiline string"""
"a\nmultiline\nstring"
julia> println(c)
hello
julia> println(e)
a multiline string
julia> using Markdown
julia> f = md"""a **markdown** _string_"""
a markdown string
Missingness implementations
Attention to these three different implementations (of 3 different concepts) of "missingness":
julia> a = nothing # C-style, "software engineer's null → run-time error
julia> b = missing # Data scientist's null → silent propagation
missing
julia> c = NaN # Not a number → silent propagation
NaN
julia> typeof(a)
Nothing
julia> typeof(b)
Missing
julia> typeof(c)
Float64
julia> d = 0/0 # a2 = mean([1,a,3]) # would error
NaN
julia> b2 = mean([1,b,3])
missing
julia> c2 = mean([1,c,3])
NaN
julia> b3 = mean(skipmissing([1,b,3]))
2.0
julia> b == missing # propagate
missing
julia> ismissing(b)
true
julia> isequal(b,2) # exception to the propagation rule, it allows a comparison when one of the item may be missing
false
julia> isequal(b,missing)
true
julia> b4 = [1,missing,3]
3-element Vector{Union{Missing, Int64}}: 1 missing 3
julia> typeof(b4)
Vector{Union{Missing, Int64}} (alias for Array{Union{Missing, Int64}, 1})
julia> eltype(b4)
Union{Missing, Int64}
julia> nonmissingtype(eltype(b4))
Int64
Random values
julia> rand() # [0,1] continuous
0.521213795535383
julia> rand(30:40) # [30,40] integer
36
julia> rand(30:0.01:40) # [30,40] with precision to the second digit
38.91
julia> using Distributions
julia> rand(Exponential(10)) # We'll see Distributions more in detail in the Scientific Programming lesson
3.5106443155652722
julia> rand(30:40,3) # A vector of 3 random numbers.
3-element Vector{Int64}: 35 34 30
julia> rand(Exponential(10),3,4) # Same syntax for sampling from any distribution !
3×4 Matrix{Float64}: 5.99452 9.34169 15.9134 1.18909 14.3277 9.82636 4.77897 5.75527 7.6609 0.507418 1.99239 5.45417
julia> using Random
julia> myRNG = MersenneTwister(123) # use StableRNG for a RNG guaranteed to remain stable between Julia-versions
Random.MersenneTwister(123)
julia> a1 = rand(myRNG,10:1000,5)
5-element Vector{Int64}: 959 364 276 490 635
julia> a2 = rand(myRNG,10:1000,5)
5-element Vector{Int64}: 511 431 906 288 550
julia> a1 == a2
false
julia> myRNG = MersenneTwister(123)
Random.MersenneTwister(123)
julia> b1 = rand(myRNG,10:1000,5)
5-element Vector{Int64}: 959 364 276 490 635
julia> b2 = rand(myRNG,10:1000,5)
5-element Vector{Int64}: 511 431 906 288 550
julia> b1 == b2
false
julia> a1 == b1
true
julia> a2 == b2
true
julia> a = rand(myRNG,Exponential(10),5)
5-element Vector{Float64}: 0.08730194128784134 34.98193102162552 2.8516248856399145 12.990776241562488 7.330776791908584
This page was generated using Literate.jl.