Initial Commit

This commit is contained in:
MarcusTL12 2019-09-12 16:32:16 +02:00
parent 3eff6439e4
commit 4adc9cd975
20 changed files with 1151 additions and 0 deletions

7
Code/1.HelloWorld.jl Normal file
View File

@ -0,0 +1,7 @@
println("Hello Julia!")
x = 3
y = 2
z = x + y
println(z)

173
Code/10.structs1.jl Normal file
View File

@ -0,0 +1,173 @@
# User defined types are made with the struct keyword. Member variables are
# listed in the struct body as follows.
# Keep in mind that julia does not support redefining of types by default,
# so whenever you change anything in the struct (field names/types, etc.)
# you have to restart the REPL.
# Alternatively you could look into the package Revise.jl which solves
# some of these problems.
struct TestType
x
y
z
end
# Every struct gets a default constructor that is the typename taking
# in all the member variables in order.
t = TestType(1, 2, 3)
# Every struct also gets a default print function that shows the variable
# in style of the constructor.
@show t
# Accessing the member variables of the struct is done simply by just
# writing variable_name.field_name
# every field is always public as julia does not implement any heavy
# object oriented design.
@show t.x
# This line is illegal since struct are immutable by default.
# t.y = 5
# Field mutability can be achieved by adding the keyword "mutable" in front.
# However for simple structs, like this xyz-point like structure, it is
# much more efficient if you can avoid making it mutable. This has to do
# with how mutable struct are allocated differently then immutable ones.
# Read more in the "Types" section of the julia documentation.
mutable struct TestType2
x::Float64
y::Float64
z::Float64
end
# Also as illustrated above, the fields in a struct can be strictly typed.
# This is usually a good idea since it makes the struct take a constant
# amount of space, and removes a lot of type bookkeping.
t2 = TestType2(1.68, 3.14, 2.71)
# With this mutable struct, the variable now behaves more like a general
# container (Array/vector) so you can change the fields.
t2.y = 1.23
@show t2
# You can also make parametric types, kind of like templates in C++.
# This essentially creates a new type for each new T, so it alleviates the
# problem of keeping track of the types of the individual fields.
struct TestType3{T}
x::T
y::T
z::T
end
# This now constructs a TestType3{Int64}
t3 = TestType3(1, 2, 3)
@show t3
# This will be the example struct throughout the rest of the file.
struct Polar{T<:Real}
r::T
θ::T
# You might want to make your own constructors for a type.
# These are typically made inside the struct body since this overrides
# the creation of the default constructor described above.
function Polar(r::T, θ::T) where T <: Real
# Doing some constructy stuff
println("Creating Polar number (r = $r, θ = )")
# The actual variable is created by calling the "new()" function
# which acts as the default constructor. This however is just
# accessible to functions defined inside the struct body.
new{T}(r, θ)
end
# You might want to implement multiple different constructors.
# This one is short and simple so it can be created with the inline syntax.
# The zero(T) function finds the apropriate "zero" value
# for the given type T. That way no implicit conversions have to be done.
Polar{T}() where T <: Real = new{T}(zero(T), zero(T))
end
# Not all constructors have to be defined inside the struct body, but here
# outside the body we no longer have access to the "new()" function since
# the compiler doesn't have any way to infer which type "new" would refer
# to out here. So instead we have to use one of the constructors we defined
# inside the struct.
# This constructor is very similar to the Polar{T}() function but instead
# of writing p = Polar{Int}() to take the type in as a template argument
# you would pass the type in as an actual argument; p = Polar(Int)
Polar(::Type{T}) where T <: Real = Polar{T}()
# Constructing with the first constructor
p = Polar(3.14, 2.71)
@show p
# Constructing with the empty constructor
p = Polar{Int}()
@show p
# Constructing with the constructor taking the type as an argument
p = Polar(Float16)
@show p
# You might want to overload som basic operators and functions on your
# type. One common thing to want to override is how your variables are printed.
# Since there are so many different ways of converting a variable to text
# (print, println, @printf, string, @show, display, etc.)
# it can be difficult to find out which function is actually responsible
# for converting to text.
# This function turns out to be the Base.show() function.
# It takes in an IO stream object and and the variable you want to show.
# Since the funtion is from the Base module
# we have to override Base.show specifically
# Also it is very important to actually specify the type of the variable here
# so that we are actually specifying the show function for our type only
# and not for any general type.
function Base.show(io::IO, p::Polar)
# Say we want to print our polar number as r * e^(i θ) style string
show(io, p.r) # non strings we want to recursively show
write(io, "^(i ⋅ ") # strings we want to write directly with write
show(io, p.θ)
write(io, ")")
end
# Now our polar numbers are printed as we specified
p = Polar(3.14, 2.71)
@show p
println(p)
display(p)
# Even converting to a string is now done with our show functon
s = string(p)
@show s
# Overloading operators is even simpler as every infix operator is just a
# function; a + b is equivalent to +(a, b) (for all you lisp lovers)
# For some reason we cannot write the Base.* inline for infix operators
import Base.*
*(p1::Polar{T}, p2::Polar{T}) where T = Polar(p1.r * p2.r, p1.θ + p2.θ)
# Multiplication between different types might also be useful
*(x::T, p::Polar{T}) where T = Polar(x * p.r, p.θ)
# Implementing the reverse mulitplication such that it becomes commutative
*(p::Polar{T}, x::T) where T = x * p
p1 = Polar(2.0, 15.0)
p2 = Polar(3.0, 30.0)
p3 = p1 * p2
@show p3
# The in-place arithmetic operators (+=, -=, *=, /=, etc.) are not actual
# operators but rather just an alias for a = a * b so they need not be
# implemented seperately. If the memory copying is a problem and you
# really have to do it in-place the way to do that would be to make some
# sort of mult!(p, x) function.
p3 *= 0.5
@show p3

27
Code/11.structs2.jl Normal file
View File

@ -0,0 +1,27 @@
# This is just a super simple example to illustrate the "call" operator.
# Simple polynomial type. The Polynomials.jl package is basically a more
# complete version of this example.
struct Poly{T}
coeffs::Vector{T}
end
# Could implement a whole bunch of operators (+, -, *, /, show, etc.)
# but that can be left as an exercise for the reader :3
# Also the Polynomials.jl package have implemented most of those.
# Here, the whole point of this example; This function basically makes
# the variable, p, act as a polynomial function on x.
function (p::Poly{T})(x::T) where T <: Number
ret = zero(T)
for i in 1 : length(p.coeffs)
ret += p.coeffs[i] * x^(i - 1)
end
ret
end
# Now any variable of type Poly can be used as if it was a function
# This gives some really clean syntax :)
p = Poly([0.0, -3.0, 0.0, 1.0]) # p = 0 - 3 x + 0 x^2 + 1 x^3
@show p(3.0)

216
Code/12.structs3.jl Normal file
View File

@ -0,0 +1,216 @@
# This is a more complete example of a linked list implementation, but exists
# mainly to illustrate how to implement indexing and iteration.
# okay, this is the dirtiest hack I have ever done in julia.
# Julia is really finicky when it comes to redefining structs.
# Usually it isn't a problem if you haven't changed anything,
# but because of the recursive inclusion in the Node_ struct it was really
# unhappy, so this basically runs this if block the first time
# and not any subsequent times.
if !isdefined(@__MODULE__, :__first__)
__first__ = true
# The node struct that actually holds the values
mutable struct Node_{T}
data::T
prev::Union{Node_{T}, Nothing}
next::Union{Node_{T}, Nothing}
end
# An alias for a type union such that we can set a node to the value nothing
# when we want to indicate the end of the list.
const Node{T} = Union{Node_{T}, Nothing}
# Since we made the alias for Node, we make this wrapper for the constructor
# of the actual Node_ object. There are probably better ways to do this
# but this works fine.
function make_node(data::T, prev::Node{T}, next::Node{T}) where T
Node_{T}(data, prev, next)
end
# The wrapper struct for the nodes. Most of the methods will be implemented
# on this type.
# It is also specified as a subtype of the abstract type AbstractArray.
# This signalises that it will mostly act like an array
# and makes it possible to pass a LinkedList into a function that is specified
# to take in objects of type AbstractArray. This gives a bunch of functions
# that needs to be implemented, and some that can be overridden. All of these
# are listed under the "Intefaces" section in the julia docs.
# Here we will implement the required methods and the methods for iteration.
mutable struct LinkedList{T} <: AbstractArray{T, 1}
head::Node{T}
tail::Node{T}
items::Int # Keeping track of how many items for quick length checking
# Implementing a default constructor
LinkedList{T}() where T = new{T}(nothing, nothing, 0)
end
end # End of the dirty hack
# Constructor to create a list from a collection of elements
function LinkedList(elems::AbstractArray{T}) where T
l = LinkedList{T}()
for e in elems
push!(l, e)
end
l
end
# Not required for AbstractArray, but makes sence to implement
function Base.push!(l::LinkedList{T}, v::T) where T
n_node = make_node(v, nothing, nothing)
if l.head === nothing
l.tail = l.head = n_node
else
l.tail.next = n_node
n_node.prev = l.tail
l.tail = n_node
# n_node.next = l.head # Uncomment this one if you feel brave ;)
end
l.items += 1
l
end
# One of the reqired functions for AbstractArray. size returns a tuple
# of all the dimensions, but this is a 1d collection so it only contains
# one element.
Base.size(l::LinkedList) = (l.items, )
# Implements the iteration function. This makes it possible to for example
# write something like "for element in list" to iterate through the list.
# The function returns a 2-tuple where the first element is the next iteration
# item and the second element is the next state. The state is some variable
# that determines the next element and state. When the function returns
# nothing, the loop is done.
function Base.iterate(l::LinkedList, state=l.head)
if state === nothing
nothing # If the state is nothing, we return nothing
else
(state.data, state.next) # Else the next item/state is returned
end
end
# Implements a simple show function for nicer printing. The default show
# function is ridiculous when you have circular/recursive containment in
# your structs, so it is necessary to implement a nicer version.
function Base.show(io::IO, l::LinkedList{T}) where T
show(io, l.items)
write(io, "-element LinkedList{")
show(io, T)
write(io, "}:\n ")
first = true
for elem in l
if !first
write(io, "")
end
first = false
show(io, elem)
end
end
# A utility function for both the getindex and setindex! functions
function findnode(l::LinkedList, i::Int)
if !(1 <= i <= length(l))
throw(BoundsError(l, i)) # Throwing an error if out of bounds
end
curnode = l.head
for j in 1 : i - 1
curnode = curnode.next
end
curnode
end
# Another required function for AbstractArray.
# This function makes it possible to read the element at a certain index
# with the syntax l[i]
Base.getindex(l::LinkedList, i::Int) = findnode(l, i).data
# The complimentary function for seting elements at a given index
# with corresponding syntax l[i] = v
Base.setindex!(l::LinkedList{T}, v::T, i::Int) where T = findnode(l, i).data = v
# Practial to override the copy function. In this case it it really
# simple to set up because of the way we made the constructor.
Base.copy(l::LinkedList) = LinkedList(l)
# Implementing the append function as well. Not necessary but practical to have.
function Base.append!(l::LinkedList{T}, l2::AbstractArray{T}) where T
for elem in l2
push!(l, elem)
end
end
# Implementing insert! for a linked list is quite useful
function Base.insert!(l::LinkedList{T}, i::Int, v::T) where T
node = findnode(l, i)
n_node = make_node(v, node, node.next)
if node.next !== nothing
node.next.prev = n_node
end
node.next = n_node
l
end
# Same goes for deleteat!
function Base.deleteat!(l::LinkedList, i::Int)
node = findnode(l, i)
if node.prev !== nothing
node.prev.next = node.next
end
if node.next !== nothing
node.next.prev = node.prev
end
l
end
# It can be useful to reverse a list
function Base.reverse!(l::LinkedList)
cur_node = l.head
for i = 1 : length(l)
cur_node.prev, cur_node.next = cur_node.next, cur_node.prev
cur_node = cur_node.prev
end
l.head, l.tail = l.tail, l.head
l
end
# It can also be useful to make a reversed copy of a list
Base.reverse(l::LinkedList) = reverse!(copy(l))
# Some test code:
# Creating a linked list
l = LinkedList(1 : 5)
@show l
# showing the third element
@show l[3]
# setting the third element
l[3] = 7
@show l
# inserting the value 8 between 2nd and 3rd node
insert!(l, 2, 8)
@show l
# deleting the second node
deleteat!(l, 2)
@show l
# since all required functions for AbstractArray have been implemented
# standard functions, such as sort!, should now work. However if performance
# is important you should probably implement your own version of functions
# so that it is done more efficiently for the collection in question.
# for example the sort function here will do a lot of index operations
# and since finding a node in a linked list is O(n) complexity and
# sort! is O(n log n) list operations, the whole runtime becomes O(n^2 log n).
sort!(l)
@show l

39
Code/13.fileIO.jl Normal file
View File

@ -0,0 +1,39 @@
# File IO is really similar to how it would be done in python and C.
io = open("test.txt", "w") # Opens test.txt for writing
write(io, "Hello World!") # writes a string to it
close(io) # closes the file
# However the more appropriate way to do it would be more similar to
# Python's "with" statement. This encloses the file handling in a code block
# and automatically closes the filestream and does cleanup if anything goes
# wrong.
open("test.txt", "w") do io
write(io, "Hello\nWorld!")
end
# The do-syntax here is really just syntactical sugar for giving the
# open() function a function as an argument
# The following syntax is effectively what the do-syntax above does.
# Create a function taking in a single argument with the file-handling code
function f(io)
write(io, "Hello\nWorld!")
end
# call the open function with this function as the first parameter
open(f, "test.txt", "w")
# This makes for some really clean and safe file handling syntax.
s = open("test.txt", "r") do io
collect(eachline(io))
end
display(s)
# for simple functions as the one above it is possible to just pass
# the the function directly in without using the do-syntax
# for a really compact one line function call
# The ∘ symbol is function composition (from mathematics)
s = open(collect eachline, "test.txt", "r")
display(s)

15
Code/14.IOBuffer.jl Normal file
View File

@ -0,0 +1,15 @@
# IOBuffers are like writing to a file in memory. They are really efficient
# when creating strings and work like any other IO object we've worked with.
io = IOBuffer()
a = 42
write(io, "i = ")
print(io, a)
# To retrieve the written data from the buffer we call take!(). This returns
# a Vector{UInt8} so to interpret it as a string we call String()
s = String(take!(io))
println(s)

35
Code/15.lambdas.jl Normal file
View File

@ -0,0 +1,35 @@
# A lambda function is made with an arrow from the arguments
# to the function body
f = x -> 2x^2
@show f(2)
# If the function takes more then one argument, parenthesis are required
g = (x, y) -> x * y
@show g(2, 3)
# This also holds for functions taking no arguments
p = () -> Float64(π)
@show p()
# Function currying is possible, so if anyone wants to do lambda calculus
# feel free.
h = x -> y -> x / y
@show h(12)(4)
# If a function has to be written over multiple lines, this can be done
# with a "begin" block.
fib = n -> begin
a, b = 0, 1
for i in 1 : n
a, b = b, a + b
end
a
end
@show fib.(0:6)
# Lambdas can be useful when passing a function as an argument
# without wanting to give it a name. Here is an example using a lambda
# instead of using the do-syntax for file handling
s = open(io -> String(read(io)), "test.txt", "r")
println(s)

25
Code/16.Macro.jl Normal file
View File

@ -0,0 +1,25 @@
# Julia comes with a lot of useful macros. Youv'e already seen the @show macro.
x = 5
@show x
# Macros are a small part of julias huge metaprogramming system.
# There exists a bunch of useful macros, both in julia itself, and in
# different packages. A macro is basically a function that takes in a list
# of arguments and generates a code block at compiletime. It is possible
# to create your own macros, but that takes some reading up on the
# metaprogramming section in the julia docs.
# Probably one of my most used macros is the @time macro
reallyslowfunction() = sleep(2)
@time reallyslowfunction()
# If you want to time a whole code block this is done with a "begin" block
@time begin
# Slow code
sleep(1)
end

21
Code/17.Broadcast.jl Normal file
View File

@ -0,0 +1,21 @@
# The broadcast operator (.) can be quite useful when dealing with arrays.
# Basically whenever an operator or function is used, you can just write
# a dot (.) before the operator/function to make it act elementwise.
# Here we elementwise add 3 to the array
a = [1, 2, 3, 4]
a .+= 3
@show a
# Here we elementwise multiply a by 2 and then elementwise add a and b.
b = a .* 2
@show b
c = a .+ b
@show c
# To elementwise use more general functions the dot symbol is placed between
# the function name and parentheses.
f(x) = 2x^2
d = f.(c)
@show d

42
Code/18.Unicode.jl Normal file
View File

@ -0,0 +1,42 @@
# Julia uses unicode characters quite heavily. Mostly it is possible to avoid
# using unicode completely, but it can make code look quite clean.
# The unicode autocompletion for vscode mostly works, but can be a bit
# unreliable, but the julia REPL (console) is also useful for writing unicode.
# There is a whole section in the Julia docs dedicated to how to input different
# unicode characters, so just search "Unicode Input" in the docs.
# We've seen a lot of different unicode symbols, here are some more useful ones.
# The infix operator for boolean xor is the ⊻ (\veebar, \xor) symbol.
b = true false
# In the LinearAlgebra package the ⋅ (\cdot) symbol is overloaded as the
# scalar product of vectors.
using LinearAlgebra
a = [1, 2] [2, 1]
@show a
# Where you would write "in" you could probably use either ∈ (\n) or ∉ (\notin)
# You can use ∈ for iteration
for i 1 : 5
# dostuff
end
# It can also be used for checking if an element is in a collection
3 [1, 2, 3, 4]
# And the notin symbol can be used to check if something isn't in the collection
2 [1, 2, 3, 4]
# The mathematical constants π (\pi) and (\euler) are defined as irrationals
# that can be cast to a numeric type and they will be calculated to the
# required precision.
# This calculates pi the the precision of a Float64
p = Float64(π)
@show p
# This will calculate the fraction closest to using Int16.
e = Rational{Int16}()
@show e

49
Code/19.Packages.txt Normal file
View File

@ -0,0 +1,49 @@
Therer are a lot of different useful packages for julia. All official
Packages can be found at https://juliaobserver.com/packages (I think)
Here are a couple of good packages i use a lot.
IJulia:
This package is required for using Julia in jupyter.
LinearAlgebra:
This package comes with julia without any need for installation and includes
a lot of linear algebra functionality, comparable to numpy.linalg.
Statistics:
Distributions, mean, standard deviations, etc...
Plots:
Creates nice plots. Has support for multiple backends (including pyplot).
PyCall:
makes it possible to import python packages into julia and write python code
in julia files. Anytime there exists a useful python package and no julia
equivalent, this package saves lives.
Primes:
I do not know what magic they put into making this package, but I have never
seen a so fast funtion for checking if a number is prime ever.
The package is useful for working with prime numbers.
Memoize:
A useful package for automatically memoizing a function. For people taking
algorithms and datastructures, this will make more sence later in the course.
Images:
Useful to handle images. Terrible slow to load the package, but once loaded
it is quite fast and very featureful.
SymPy:
A wrapper for pythons sympy package which is arguably much better than
sympy itself.
Printf:
Wrapper for the C- printf style functions for fast string formating.
CSV:
Package for reading/writing .csv (comma seperated values) files.
JSON:
Package for reading/writing .json files.

31
Code/2.functions.jl Normal file
View File

@ -0,0 +1,31 @@
# Standard function definition
function f(x, y)
return x + y
end
# Function implicitly returns the value of the last line
# of the function so no return keyword is required
function g(x, y)
x - y
end
# Functions can be defined one one line like this.
# This is just a shorthand and is compiled identically to the ones above
h(x, y) = x * y
# Can enforce types on function arguments and return value
# This makes it possible to create multiple functions
# with the same name but different types; These are called methods
f(x::Float64, y::Float64)::Float64 = x / y
# Runs the generic method of the function at the top of the file
println(f(2, 3))
# Runs the specific method defined for two Float64.
println(f(2.2, 3.2))
# Also falls back the the generic method as input is (::Float64, ::Int64)
println(f(1.6, 3))

41
Code/3.arithmetic.jl Normal file
View File

@ -0,0 +1,41 @@
# Also possible to enforce types on local variables
function main()
# Int is an alias for Int32 or Int64 depending on your installation
# 32 bit vs 64 bit
a::Int = 14
b::Int = 7
# Enforces the type of c for the rest of the scope
c::Int = a + b
@show typeof(c) c # The @show macro is basically a debug print
# Normal division of integers returns Float64
# Assignment to c tries to convert result to the
# type of c as the type is enforced.
# If unable to convert to Int it throws an appropriate exception
c = a / b
@show typeof(c) c
# // is rational division. This returns a rational number
c = a // b
@show typeof(c) c
# Integer division is done by div, fld or cld (floor divide/ceil divide)
# div rounds towards zero (1.5 -> 1, -2.7 -> -2)
# floor rounds downwards (1.5 -> 1, -2.7 -> -3)
# ceil rounds upwards (1.5 -> 2, -2.7 -> -2)
c = div(a, b)
c = fld(a, b)
c = cld(a, b)
# Exponentiation is done with the ^ symbol as in most calculator programs
c = a^b
@show c
end
main()

84
Code/4.strings.jl Normal file
View File

@ -0,0 +1,84 @@
# Strings are made with double quotes ""
# Single quotes '' are reserved for single characters (i.e. c/c++)
s = "Hello world"
println(s)
# When converting to a string the function string (with lower case s)
# is used.
a = 3.14
s = string(a)
println(s) # Prints 3.14
# The length of a string can be found with the length function
# similar to len in python
@show length(s)
# The function String (with upper case S) is used for more direct
# interpretation of data as a string
a = UInt8[65, 66, 67] # ASCII codes for "ABC"
println(String(a)) # Prints ABC
println(string(a)) # Prints UInt8[0x41, 0x42, 0x43]
# When converting from string to some numeric value, the parse function
# is used. This function takes in the type to try to parse to as well
# as the string to parse.
s = "128"
a = parse(Int, s)
@show a
s = "3.14"
a = parse(Float64, s)
@show a
# Easy inline string formating can be done with the $ (eval) symbol
a, b = 3, 5
s = "a, b = $a, $b" # generates string "a, b = 3, 5"
println(s)
s = "a + b = $(a + b)" # "a + b = 8"
println(s)
# However it is usually faster (performance wise) to just pass in
# multiple arguments to f.exs. println
println("a + b = ", a + b)
# The raw string macro is very useful when copying raw text and not
# wanting to worry about special characters doing special stuff
# (i.e. \n for new line). A common use for this is filepaths
filepath = raw"C:\Users\somefolder\somefile.txt"
@show filepath
# String concatenation is, somewhat weirdly, done with the * sign
# instead of the + sign. Julia's justification for this is that
# addition (+) is reserved for commutative operations (a + b = b + a)
# and since string concatenation is not commutative it gets the multiplication
# symbol instead.
s = "foo" * "bar"
println(s)
# This by extension means that if you want to repeat a string n times
# this is done with the exponentiation (^) sign
s = "foo"^5
println(s)
# To get input from the console this can be done with the readline() function
# However getting console input when running in vscode with F5/shift+enter
# seems to crash for some reason, so avoid console input if thats how you
# run the program
# s = readline()
# println(s)

133
Code/5.arrays.jl Normal file
View File

@ -0,0 +1,133 @@
# Lists/Arrays/Vectors work very similarly to python
# A list can be constructed from a set of elements with [] brackets
l = [1, 2, 3, 4]
@show typeof(l)
# Indexing the array can be done with [] also.
# Keep in mind, arrays in julia are 1-indexed by default
@show l[2]
# The type of elements in the array can be enforced by writing
# the type directly in front of the brackets
l = Int32[1, 2, 3, 4]
@show typeof(l)
# An array will try to implicitly convert data to have a single
# element data type
l = [1, 2.3, 3//2]
@show l
@show typeof(l)
# However if this is not possible, the list will get the type Any.
# Arrays with multiple types are much more inefficient because of
# type bookkeeping.
l = [1, 2.3, 3//2, "hello"]
@show l
@show typeof(l)
# Julia natively supports multidimensional arrays, such as matrices, tensors
# etc. with a lot of linear algebra built into the language.
m = [
1 2 3;
4 5 6;
7 8 9
]
display(m) # display() is a pretty-print function that is nice for showing
# matrices and other containers
@show typeof(m)
# Creating an "empty" array can be done in a couple different ways
# Creating an array with no elements is usually done with empty brackets []
l = []
# or with a specific type
l = Float32[]
# Creating an uninitialized array with a set amount of elements
# can be achieved by calling the Array constructor
l = Array{Int, 1}(undef, 100) # Creating a 1d array of 100 undefined elements
# when working with 1d arrays it is usually better to use the alias Vector
# for 1d array. Vector{T} is an alias for Array{T, 1}
l = Vector{Int}(undef, 100) # Does the exactly the same as line above
# however it is usually cleaner and safer to use the function zeros() or
# ones() to initialize the memory.
l = zeros(Int, 100) # creates an array of 100 zeros of type Int
l = ones(Int, 100) # same for ones
# Values can be added to the back of an array with the push! function.
l = [1, 2, 3, 4]
push!(l, 5)
@show l
# Reserving space in the buffer can be done with sizehint!
# That way pushing values to the array doesn't cause so many reallocations
sizehint!(l, 100) # reserving space for 100 elements
# Pushing to the front can be done with pushfirst!
pushfirst!(l, 0)
@show l
# Do not confuse push! with append!. Where append in python works as push!
# here, append! in julia concatenates a whole array to the back.
append!(l, [6, 7, 8, 9])
@show l
# Deleting an element at a specific index is done by deleteat!
# Keep in mind that deleting elements from an array is often slow
# as data has to be moved. In julia it is fast to delete elements near
# either end of the array as the shortest end is the one moved to fill
# the space.
deleteat!(l, 3)
@show l
# As in python, list can be created with a list comprehension
l = [i^2 for i in 1 : 5] # creates square numbers from 1 to 25
@show l
# As Julia supports multidimensional arrays, it also supports
# multidimensional list comprehensions
l = [x * y for y in 1 : 3, x in 1 : 4]
display(l)
# It is possible to view an array through a wrapper, making it differently
# organized without copying data, f.exs. the transpose of a matrix or
# viewing a multidimensional array as the raw memory as a 1d array.
# There are many different types of views in multiple different packages.
# search the documentation to see more
l2 = view(l, 3:-1:1, 1 : 4) # Flipping the matrix upside down
display(l2)
l2 = transpose(l) # transpose is a wrapper for a PermutedDimsArray
l2 = PermutedDimsArray(l, (2, 1)) # flips x and y dimensions
display(l2)
# Random numbers can be created in many different ways. Here are a fiew
# easy ones.
a = rand(Int64) # Create a random Int64
a = rand(Float64) # Create random Float64 in the range 0 : 1
a = rand(1 : 10) # Create a random Int in the given range
l = rand(1 : 10, 3, 4) # Create a random 3x4 matrix with entries in 1 : 10
display(l)
using Random
rand!(l, -100 : 100) # Fill l with random entries from -100 : 100
display(l)

45
Code/6.dicts.jl Normal file
View File

@ -0,0 +1,45 @@
# Dicts (dictionaries/maps) are not as simple as in python, resembling
# more the way modern c++ does it
# Dicts are usually created from an array of "Pair" types (First => Last)
# The type of the dict keys and values are automatically infered.
d = Dict([
"foo" => 3,
"bar" => 7
])
display(d)
# A dict can also be initialized as an array of two tuples. This works
# identically to the example above
d = Dict([
("foo", 2.71),
("bar", 3.14)
])
display(d)
# Values can be pushed to the dict similarly to an array.
# Keep in mind that creating an empty dict like this gives the type
# Dict{Any, Any} which might be somewhat slower because of having
# to work for general types
d = Dict()
push!(d, "foo" => 1//2)
push!(d, "bar" => 22//7)
display(d)
# The problem above can be mitigated by infering the types manually
d = Dict{String, Rational{Int}}()
push!(d, "foo" => 1//2)
push!(d, "bar" => 22//7)
display(d)
# A key can be deleted from a dict with delete!()
delete!(d, "foo")
display(d)
# To check if a certain key exists in the dict, the haskey function is used
println(haskey(d, "foo")) # false
println(haskey(d, "bar")) # true
# Indexing a dict is done with brackets like for any array
println(d["bar"])

47
Code/7.sets.jl Normal file
View File

@ -0,0 +1,47 @@
# Sets are created from lists of elements and duplicates are removed
s = Set(['a', 'b', 'c', 'b'])
display(s)
@show 'b' in s
# As with other collections, elements can be added with push!
push!(s, 'd')
display(s)
# And elements are removed with delete!
delete!(s, 'b')
display(s)
# An empty set can be made just like an empty dict, but again this forces
# it to accept elements of type Any, which can be an minor slowdown
s = Set()
# This is of course fixed by infering the type when creating the dict
s = Set{Int}()
push!(s, 2)
display(s)
# Usual set operations like union, intersect, difference and checking if
# one is a subset of another, are all implemented, many with
# unicode infix operators
s1 = Set([1, 2, 4, 8, 16])
s2 = Set([2, 3, 5, 16])
@show union(s1, s2) # Takes the set union
@show s1 s2 # Equvialent to the line above (\cup for the unicode macro)
@show intersect(s1, s2) # Set intesect
@show s1 s2 # Unicode version (\cap)
@show setdiff(s1, s2) # Set difference. No unicode replacement for this one
@show symdiff(s1, s2) # symetric difference. No unicode here either
union!(s1, s2) # Equvialent to s1 = s1 s2
@show s1
intersect!(s1, s2) # Equvialent to s1 = s1 ∩ s2
@show s1
# Check if s1 is a subset of s2. (\subseteq)
@show s1 s2

61
Code/8.controllflow.jl Normal file
View File

@ -0,0 +1,61 @@
a = 3
b = 5
# if statements are made very similarly to other languages like python
if a < b
println("<")
elseif a > b
println(">")
else
println("=")
end
i = 10
# While loops are made similarly to if statements
while i >= 0
print(i, ' ')
global i -= 1
end
println()
l = []
# A typical range based for loop is made similarly to python
# with matlab style range syntax
for i in 1 : 10
push!(l, i^2)
end
println(l)
# A for-each style for loop is also similar to python
for i in l
print(i, ' ')
end
println()
# Reverse ranges can be achieved by specifying the steplength as the
# middle argument in the range
for i in length(l) : -1 : 1
print(l[i], ' ')
end
println()
# A reverse for-each loop can be achieved a couple different ways.
# Perhaps the cleanest way is to just create the reverse array and
# iterating over that. This however copies the whole array to a new
# reversed one, so it is a bit memory inefficient.
for i in reverse(l)
print(i, ' ')
end
println()
# This problem can be mitigated by using an array view that views the
# array in reverse. This is fine, but it doesn't look as clean anymore.
for i in view(l, length(l) : -1 : 1)
print(i, ' ')
end
println()

59
Code/9.scopes.jl Normal file
View File

@ -0,0 +1,59 @@
# The global scope in julia behaves weirdly. This section shows how to
# cicumvent the issues that arise, but the bottom line here should be
# to avoid the global scope as much as humanly possible.
# Changing globals require ugly explicit syntax, globals cannot be
# strongly typed, and, in general, globals are significantly slower
# up to several orders of magnitude in some cases.
a = 5
# This works
if a > 2
println(a)
a += 3
end
for i in 1 : 3
println(a) # This works
# a -= 1 # This does not work
end
# Declaring a to be global fixes this problem
for i in 1 : 3
global a
println(a)
a -= 1
end
# None of this is a problem in functions, so do everything in functions
function main()
a = 7
for i in 1 : 3
a -= 1
end
println(a)
end
main()
# An inline function body can be created with a "let" block.
# This creates a nameless function that is run immediately.
let a = 10
for i in 1 : 3
a -= 1
end
println(a)
end
# A "begin" block is similar to a let block, but it does not enforce
# a new scope, so this code is still in the global scope
begin
a = 12
for i in 1 : 3
global a -= 1
end
println(a)
end

1
LiveCode/test.jl Normal file
View File

@ -0,0 +1 @@
println("Hello World!")