Introduction to Julia for Ruby and Python Programmers

This is an introduction to the Julia programming language for folks with previous experience in either Ruby or Python. As you go through this post you will notice similarities with both Ruby and Python syntax.

This is just the tip of the iceberg but we have to start with step 1 haven’t we?

Without further ado

Basics

println("Hello World from Julia Language!!!")
Hello World from Julia Language!!!
# This is a single linecomment
#=
 This is a multiline comment
 Assign variables as you would in Python or Ruby
=#
name = "John Doe"
_answer = 42
# can use cool letters :-)
π = 3.1415
# use typeof() to check variable types
println(typeof(name))
println(typeof(_answer))
println(typeof(π))
String
Int64
Float64

Help and searching for help

?
search: ⊻ ⊋ ⊊ ⊉ ⊈ ⊇ ⊆ ≥ ≤ ≢ ≡ ≠ ≉ ≈ ∪ ∩ ∛ √ ∘ ∌ ∋ ∉ ∈ ℯ ÷ ~ | ^ \ > < : / - + *

Welcome to Julia 1.4.2. The full manual is available at

https://docs.julialang.org/

as well as many great tutorials and learning resources:

https://julialang.org/learning/

For help on a specific function or macro, type ? followed by its name, e.g. ?cos, or ?@time, and press enter. Type ; to enter shell mode, ] to enter package mode.

?log10
search: log10 log1p
log10(x)

Compute the logarithm of x to base 10. Throws DomainError for negative Real arguments.

Examples

julia> log10(100)
2.0

julia> log10(2)
0.3010299956639812

julia> log10(-2)
ERROR: DomainError with -2.0:
NaN result for non-NaN input.
Stacktrace:
 [1] nan_dom_err at ./math.jl:325 [inlined]
[...]

Basic Math

sum = 10 + 3
diff = 5 - 4
product = 5 * 4
division = 21 / 5
power = 10 ^ 2
modulus = 10 % 2
0

Strings

s1 = "The quick brown fox jumps over the lazy dog"
"The quick brown fox jumps over the lazy dog"
s2 = "The quick brown "fox" jumps over the lazy dog" # can't use double quotes inside a string unless...
syntax: cannot juxtapose string literal
s3 = """The quick brown "fox" jumps over the lazy dog""" # Use triple quotation when you need to use double quotes in string
"The quick brown \"fox\" jumps over the lazy dog"
# single quotes are not good for creating strings
s4 = 'Hello World'
syntax: invalid character literal
# but are ok for defining single characters
c = 'a'
typeof(c)
Char

String interpolation

villain = "Darth Vader"
real_name =  "Anakin Skywalker"
f = "$villain real name is $real_name and he has defeated more than $(2 ^ 10) foes"
println(f)
Darth Vader name is Anakin Skywalker and has defeated more than 1024 foes

String Concatenation

There are 3 ways we can use to concatenate strings

Option 1: string() converts non-strings inputs to string

string("1 + 2 = ",1+2)
"1 + 2 = 3"
string("The universe is currently estimated at roughly ", 13.8," billion years old, give or take ", 130," million years.")
"The universe is currently estimated at roughly 13.8 billion years old, give or take 130 million years."

Option 2: use *

villain = "Darth Vader"
real_name =  "Anaking Skywalker"
c = 100
villain*" is "*real_name
"Darth Vader is Anaking Skywalker"

Option 3: Use interpolation

"$villain$real_name"
#=
For those used to Python/Ruby the following wont work
as you would expect:
"abc"*3
'a'*2
=#
"Darth VaderAnaking Skywalker"

Data Structures

Dictionaries

# Very similar to Ruby Hash
grades = Dict("Jane Doe" => 10, "Jim Doe" => 6)
Dict{String,Int64} with 2 entries:
  "Jane Doe" => 10
  "Jim Doe"  => 6
grades["Helen Doe"] = 9
grades
Dict{String,Int64} with 3 entries:
  "Jane Doe"  => 10
  "Jim Doe"   => 6
  "Helen Doe" => 9
deleted = pop!(grades,"Helen Doe") # use pop! to retrieve the grade of Helen Doe and delete her from Dict
9
grades
Dict{String,Int64} with 2 entries:
  "Jane Doe" => 10
  "Jim Doe"  => 6

Tuples

# Tuples are inmutable like in Python
record = ("GOOG", 100, 490.1)
("GOOG", 100, 490.1)
## index starts at 1 like R
println(record[1])
println(record[2])
println(record[3])
GOOG
100
490.1
symbol, shares, price = record
("GOOG", 100, 490.1)
symbol
"GOOG"
shares
100
price
490.1

Arrays

# Like Python or Ruby
fibonnaci = [1,1,2,3,5,8,13] # Int64 Array
7-element Array{Int64,1}:
  1
  1
  2
  3
  5
  8
 13
# This is an interesting type "Any" when you mix strings and numbers
ary = [1, "two", 3.0]
3-element Array{Any,1}:
 1
  "two"
 3.0
# another way to create arrays is start:steps:end
w = -5:0.5:3 # use this format if you do not need to materialize the Array - Very fast
-5.0:0.5:3.0
w = [-5:0.5:3;] # semi-colon at the end is mandatory to expand the range
17-element Array{Float64,1}:
 -5.0
 -4.5
 -4.0
 -3.5
 -3.0
 -2.5
 -2.0
 -1.5
 -1.0
 -0.5
  0.0
  0.5
  1.0
  1.5
  2.0
  2.5
  3.0
w = collect(-5:1.5:3)
6-element Array{Float64,1}:
 -5.0
 -3.5
 -2.0
 -0.5
  1.0
  2.5
# Get Array element by index
ary[2] # 2nd element in array
ary[length(ary)] # last element in array
ary[2:3] # 2nd and 3rd element - it does include the last element in range
2-element Array{Any,1}:
  "two"
 3.0
# Update value
ary[2] = 2
2
ary
3-element Array{Any,1}:
 1
 2
 3.0
#Add another item at the end of the array
push!(ary,23)
4-element Array{Any,1}:
  1
  2
  3.0
 23
# remove last item in array , works exactly like pop! for dictionaries
pop!(ary)
23
# Multidimensional arrays
## 2D array
a = [['a','b','c'],['d','f','g']]
2-element Array{Array{Char,1},1}:
 ['a', 'b', 'c']
 ['d', 'f', 'g']
## Another 2D array
rand(2,2)
2×2 Array{Float64,2}:
 0.713569  0.284151
 0.878189  0.279176
## 3D array
rand(4,3,2)
4×3×2 Array{Float64,3}:
[:, :, 1] =
 0.708415  0.504919  0.794934
 0.272172  0.358612  0.420427
 0.425896  0.796454  0.254046
 0.285102  0.863149  0.000841982

[:, :, 2] =
 0.256637  0.405719  0.542334
 0.998471  0.903056  0.82045
 0.112072  0.728004  0.721563
 0.617745  0.400806  0.842085

Loops

# While Loop
n = 0
while n<3
    println("Hello ",n)
    n+=1
end
Hello 0
Hello 1
Hello 2
# for loops
sum = 0
for value in 1:10
    sum+=value
end
println(sum)
55
# Another example
for name in ["Alberto","Eduardo","Alejandro"]
    println(name)
end
Alberto
Eduardo
Alejandro
# Special Julia case
sum = 0
for value = 1:10
    sum+=value
end
println(sum)
55
# Another special case
## Difficult to get the character
for value  1:3
    println(value)
end
1
2
3
# Nested for loops
x,y = 3,2
for i = 1:n
    for j = 1:y
        println((i,j))
    end
end
(1, 1)
(1, 2)
(2, 1)
(2, 2)
(3, 1)
(3, 2)
# Nested for loops - Very similar to Python list comprehension
## Well played Julia creators!!!
for i in 1:x, j in 1:y
    println((i,j))
end 
(1, 1)
(1, 2)
(2, 1)
(2, 2)
(3, 1)
(3, 2)
# Experiment
[(i,j) for i in 1:x for j in 1:y]
6-element Array{Tuple{Int64,Int64},1}:
 (1, 1)
 (1, 2)
 (2, 1)
 (2, 2)
 (3, 1)
 (3, 2)

It worked!!!

Conditionals

balance = 500

if balance < 0
    println("Balance is below 0, add funds now or you will be charged a penalty.")
elseif balance == 0
    println("Balance is equal to 0, add funds soon.")
else
    println("Your balance is 0 or above.")
end
Your balance is 0 or above.
# Ternary operator
var = 5
var > 3 ? true : false
true

Comparison Operators

10 == 10      # true
'a' == "a"    # false
"b" > "a"     # true
-5 <= 5       # true
true!=false   # true
5  10        # false
"A" > "b"     # false
true && true  # true - logical and
false || true # true - logical or
true

Bitwise Operators

Operator Name
~x bitwise not
x & y bitwise and
x | y bitwise or
x ⊻ y bitwise xor (exclusive or)
x >>> y logical shift right
x >> y arithmetic shift right
x << y logical/arithmetic shift left

Arithemtic Operations

A complete set of operations

x=10
y=10
+x       # unary plus
-x       # unary minus
x + y    # performs addition
x - y    # performs subtraction
x * y    # performs multiplication
x / y    # performs division
x ÷ y    # integer divide: x / y, truncated to an integer
x \ y    # inverse divide  equivalent to y / x
x ^ y    # raises x to the yth power
x % y    # remainder -equivalent to rem(x,y)
;        # no output in jupyter

Functions

function greeting(name)
    println("Hello $name")
end
greeting("Joe")
Hello Joe
# return the last declared variable k.
function test()
   i = 100
   j = 10
   k = 0
end
test()
0
# Alternative way for declaring function in single line
greeting2(name) = println("Hello $name")
is_even(value) =  value % 2 == 0
is_even (generic function with 1 method)
greeting2("Paul")
Hello Paul
is_even(10)
true
# anonymous functions
greeting3 = name -> println("Hello $name")
is_odd = n -> n%2 != 0
#9 (generic function with 1 method)
greeting3("Ed")
Hello Ed
is_odd(10)
false

Mutating vs Non-mutating functions

Methods that end in ! indicate that the function will modify the object it’s called on.This is exactly how some methods in Ruby work. These functions are labelled destructive/dangerous methods because they change state that someone else might have a reference to. Here’s a simple example for arrays:

a = [9,8,12]
3-element Array{Int64,1}:
  9
  8
 12
sort(a)
3-element Array{Int64,1}:
  8
  9
 12
a # original array has not been modified
3-element Array{Int64,1}:
  9
  8
 12
sort!(a)
3-element Array{Int64,1}:
  8
  9
 12
a # original array has been modified!
3-element Array{Int64,1}:
  8
  9
 12

Broadcasting

Broadcasting in Julia is a way of writing vectorised code. In other word, rather than taking a vector as a a whole and apply a function Broadcasting will apply the function to each element of the vector.

Let’s see this with an example:

# Return true is n < 0
is_negative = n -> n < 0
#11 (generic function with 1 method)
is_negative([0,-1,-5,4]) # Throws an error as this operation is invalid in a vector
MethodError: no method matching isless(::Array{Int64,1}, ::Int64)
Closest candidates are:
  isless(!Matched::Missing, ::Any) at missing.jl:87
  isless(!Matched::AbstractFloat, ::Real) at operators.jl:158
  isless(!Matched::Real, ::Real) at operators.jl:346
  ...



Stacktrace:

 [1] <(::Array{Int64,1}, ::Int64) at ./operators.jl:268

 [2] (::var"#11#12")(::Array{Int64,1}) at ./In[64]:2

 [3] top-level scope at In[65]:1
# with Broadcasting the function is apply to each element in the array
is_negative.([0,-1,-5,4])
4-element BitArray{1}:
 0
 1
 1
 0

Packages

Repositories:

Note: If you are using JuliaPro your package server may point to a different location

Loading a package

This is similar to import in Python and require in Ruby

using Pkg

Installing a new Package

To install a new package you need first to load the Pkg package (see above). Use Pkg.add("package_name") to install it.

Pkg.add("SQLite")
   Updating registry at `~/.julia/registries/General`


[?25l    

   Updating git-repo `https://github.com/JuliaRegistries/General.git`


[?25h[1mFetching: [========================================>]  100.0 %

  Resolving package versions...
   Updating `~/.julia/environments/v1.4/Project.toml`
 [no changes]
   Updating `~/.julia/environments/v1.4/Manifest.toml`
 [no changes]

Plotting

using Plots # Pkg.add("Plots")
y = 0.01:0.05:0.99
logit(x) = log(x/(1-x))
x = logit.(y);
# load the gr() backend
gr()
Plots.GRBackend()
plot(x,y,label = "line")
scatter!(x,y, label = "points") # using ! to modify the previous plot

svg

# Had to install Pkg.add("ORCA") to make plotlyjs() work correctly
plotlyjs()
plot(x,y,label = "line")
scatter!(x,y, label = "points") 
Plots.jl
# Subplots
x = -3:0.1:3
p1 = (x,x)
p2 = (x, x.^2)
p3 = (x, x.^3)
p4 = (x, x.^4)
plot([p1,p2,p3,p4], layout=(2,2), legend=false)
Plots.jl

That’s all folks - Hope you have enjoyed this long but summarised introduction to Julia

Comments

comments powered by Disqus