UQ MATH2504
Programming of Simulation, Analysis, and Learning Systems
(Semester 2 2024)


Main MATH2504 Page

Unit 1 | Unit 2 | Unit 3 | Unit 4 | Unit 5 | Unit 6 | Unit 7 | Unit 8

Unit 1: Hello World Bootcamp

In this unit we'll study the basic of a programming language, Julia in our case. The unit ends with suggested class micro-projects and practice questions for the practice quiz that is at the end of the unit. The pace and nature of this unit is aimed at students with little or no programming experience. The units afterwards pick up the pace significantly.

Super simple hello world

Here is a Julia statement:

println("Hello World!")
Hello World!

We are calling (or invoking) the function println with the argument "Hello World!". Round brackets after a name are the application of a function, kind of like in mathematics. But in Julia, round brackets also have other uses:

For example, dealing with the order of precedence of computations:

1 + 1 * 3
4
(1 + 1) * 3
6

And also creating tuples:

my_tuple = ("hello", 35, π, "world", '!')
typeof(my_tuple)
Tuple{String, Int64, Irrational{:π}, String, Char}

This whole notebook is created using a package called Weave.jl. But try running these simple commands in:

  • REPL (by running Julia and using the REPL, line after line)

  • Jupyter (in a Jupyter notebook with Ctrl+Enter)

  • In a file using (by creating a file such as mycode.jl and running `julia mycode.jl`)

  • In VS-Code with the Julia plug-in

Getting to business - what to expect from this unit:

  • Variables

  • Types

  • Using in-built functions (e.g. println)

  • Using in-built macros (e.g. @show)

  • Logical statements (AND, OR, etc..)

  • Conditional statements (if-elseif-else)

  • For loops

  • While loops

  • Creating your own functions with (function)

Variables and types

x = 3
3
x + 25
28
typeof(x)
Int64
sizeof(x)
8
isbits(x)
true
y = "cats"
"cats"
typeof(y)
String
sizeof(y)
4
isbits(y)
false
y = "η" # \eta + [TAB]
"η"
sizeof(y)
2
y = "p"
"p"
sizeof(y)
1
y = 'p'
'p': ASCII/Unicode U+0070 (category Ll: Letter, lowercase)
typeof(y)
Char
isbits(y)
true

Operations with Variables

x = "hello " #notice the extra space
y = "world"
x*y #In Python it would have been x+y for concatenation
"hello world"
x = 2
y = 5
x^y #In Python it would have x**y (raising to a power)
32
x = "hello "
y = 5
x^y
"hello hello hello hello hello "
x[1:end-1]
"hello"
x^(y-1)*x[1:end-1]
"hello hello hello hello hello"

There were invisible brackets around x^(y-1):

(x^(y-1))*x[1:end-1]
"hello hello hello hello hello"

What if we did the brackets the other way?

x^((y-1)*x[1:end-1])
ERROR: MethodError: no method matching *(::Int64, ::String)

Closest candidates are:
  *(::Any, ::Any, !Matched::Any, !Matched::Any...)
   @ Base operators.jl:587
  *(::Real, !Matched::Complex{Bool})
   @ Base complex.jl:327
  *(!Matched::Missing, ::Union{AbstractChar, AbstractString})
   @ Base missing.jl:184
  ...

What causes the error?

x = 2
x #\sqrt + [TAB]
1.4142135623730951
sqrt(x)
1.4142135623730951
round(x, digits = 3)
1.414
y = -x
-2
sqrt(y)
ERROR: DomainError with -2.0:
sqrt was called with a negative real argument but will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).
z = sqrt(y + 0im)
0.0 + 1.4142135623730951im
x = 20
y = 5 

#swapping x and y:  attempt 1
x = y
y = x

@show x #This is the @show macro
@show y;
x = 5
y = 5
x = 20
y = 5 

#swapping x and y:  attempt 2
temp = x
x = y
y = temp

@show x
@show y;
x = 5
y = 20

In Julia we can do it without using the variable temp?

x = 20
y = 5 

x, y = y, x #Julia specific solution

@show x
@show y;
x = 5
y = 20

Trick Challenge: Can you do it without using the variable temp?

x = 20
y = 5 

y = x + y # (x, y) becomes (20, 25)
x = y - x # (x, y) becomes (5, 25)
y = y - x # (x, y) becomes (5, 20)

@show x
@show y;
x = 5
y = 20

Using in-built functions (e.g. println)

So we have seen in-built functions like println and sqrt, but surely there are many more?

println("hello world")
hello world

The println function can also take several arguments.

println("hello", " ", "world")
hello world

Here is print without a new line.

print("hello")
print(" ")
print("world")
hello world
println("hello\nworld") #notice the \n (newline)
println("Here is the next line")
hello
world
Here is the next line

An equivalent use of print:

print("hello")
print("\n")
print("world")
print("\n")
println("Here is the next line")
hello
world
Here is the next line

Let's see some mathematical in-built functions:

exp(1)
2.718281828459045
 #\euler + [TAB]
ℯ = 2.7182818284590...
log(^2), log2(2^5), log10(10^3)
(2.0, 5.0, 3.0)
abs(-3.5), abs(3.5), abs(3 + 4im)
(3.5, 3.5, 5.0)

But you can't do absolute value of a string:

abs("3.5")
ERROR: MethodError: no method matching abs(::String)

Closest candidates are:
  abs(!Matched::Bool)
   @ Base bool.jl:153
  abs(!Matched::Pkg.Resolve.VersionWeight)
   @ Pkg ~/.julia/juliaup/julia-1.10.4+0.x64.apple.darwin14/share/julia/stdlib/v1.10/Pkg/src/Resolve/versionweights.jl:32
  abs(!Matched::Missing)
   @ Base missing.jl:101
  ...

factorial(4)
24

Logical statements (AND, OR, etc..)

true, false #In Python it is True and False (caps first letter)
(true, false)
5 == 2 + 3 # check for equality
true
5 != 2 + 3 # check for not being equal (!=)
false
false && false, false && true, true && false, true && true #logical AND
(false, false, false, true)
false || false, false || true, true || false, true || true #logical AND
(false, true, true, true)
(2 != 3) || (2 == 3)
true
!(2 == 3) # ! (not)
true
using Random
Random.seed!(7)

x = rand(1:100) #random number within 1, 2, ..., 100
y = rand(1:100) #random number within 1, 2, ..., 100

@show x, y

(x == y) || (x != y)
(x, y) = (25, 72)
true
x < y, x<=y, x  y, x > y, x  y # \le or \ge + [TAB]
(true, true, true, false, false)
(x == y) && (x != y)
false

Conditional statements (if-elseif-else)

if 2 < 3 && 2 > 3
    println("The world has gone crazy")
end
if 2 < 3 || 2 > 3
    println("The world makes sense")
end
The world makes sense
x = 25.3
if x < 30
    println("It is less than 30")
else
    println("It is greater or equal to 30")
end
It is less than 30
x = 25.3
if x < 20
    println("It is less than 20")
elseif x < 30
    println("It is less than 30 but not less than 20")
else
    println("It is greater or equal to 30")
end
It is less than 30 but not less than 20

Let's use it to compute an absolute value.

x = -3 # some input
if x < 0
    println(-x)
else
    println(x)
end
3

For loops

for i in 1:5
    println(i)
end
1
2
3
4
5
for i in 1:3
    println(i)
end
1
2
3
for i  1:3 # \in + [TAB]
    println(i)
end
1
2
3
for _  1:3 # \in + [TAB]
    println("hello")
end
hello
hello
hello
total = 0
max_val = 10
for i  1:max_val 
    global total # This line can be ignored by now
    total = total + i
end
println("The total is: $total")
println("And using a formula: ", max_val*(max_val+1)/2)
The total is: 55
And using a formula: 55.0

Notice the above 55 vs. 55.0. This is because of division which converts an integer to a float.

While loops

i = 1
while i  3
    global i
    println(i)
    i = i + 1
end
1
2
3

The hailstone sequence: If a number if even, half it, if it is odd, multiply by 3 and add 1. Stop when you reach 1.

n = 7
while n != 1
    global n
    print(n, ", ")
    if n % 2 == 0 # modulo
        n = n ÷ 2 # \div + [TAB] Integer division
    else
        n = 3n + 1
    end
end
println(n)
7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1

Creating your own functions with (function)

We already had logic for an absolute value function (of real values). Now let's make a function out of it:

function my_abs(x)
    if x < 0
        return -x
    else
        return x
    end
end
my_abs (generic function with 1 method)

We can also implement this as,

function my_abs(x)
    if x < 0
        return -x
    end
    return x
end
my_abs (generic function with 1 method)

Or even,

function my_abs(x)
    if x < 0
        return -x
    end
    x
end
my_abs (generic function with 1 method)
@show my_abs(-3.5)
@show my_abs(2.3)
@show my_abs(0);
my_abs(-3.5) = 3.5
my_abs(2.3) = 2.3
my_abs(0) = 0

Functions don't have to have an argument or a return value:

function print_my_details()
    println("Name: Jacob")
    println("Occupation: diesel mechanic")
    return nothing
end

print_my_details()
Name: Jacob
Occupation: diesel mechanic

Functions can be combined.

function times_2(x)
    return 2x
end

times_2(times_2(times_2(10)))
80

When you can implement a function in one line, you can avoid using the function keyword.

times_2(x) = 2x #this defines the function 

times_2(times_2(times_2(10))) #this uses the function.
80

In Python you use the def keyword to define (write) functions.

Let's see the hailstone sequence for the first 10 integers:

#let's "wrap" the code we had before in a function
function hailstone(n_start)
    n = n_start
    while n != 1
        print(n, ", ")
        if n % 2 == 0 
            n = n ÷ 2 
        else
            n = 3n + 1
        end
    end
    println(n)
    return nothing
end

hailstone(7)
7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1
println("Hailstone sequences: ")
for n in 1:10
    hailstone(n)
end
Hailstone sequences: 
1
2, 1
3, 10, 5, 16, 8, 4, 2, 1
4, 2, 1
5, 16, 8, 4, 2, 1
6, 3, 10, 5, 16, 8, 4, 2, 1
7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1
8, 4, 2, 1
9, 28, 14, 7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1
10, 5, 16, 8, 4, 2, 1

Arrays = Data

So up to this point we have the basics of programming including variables, logical statements, conditional statements, loops, and functions. One other major component is data. For this let us consider arrays.

[2, 4, -3, 15] #this is an array
4-element Vector{Int64}:
  2
  4
 -3
 15

It is a "chunk of memory" (RAM).

a = [2, 4, -3, 15]
b = a #This just makes b point to the same chunk of memory as a.
@show pointer(a)
@show pointer(b);
pointer(a) = Ptr{Int64} @0x000000011576cac0
pointer(b) = Ptr{Int64} @0x000000011576cac0

You can see that both a and b "point" to the same location in memory.

We can get an element of an array by "indexing". In Julia (like MATLAB and R) indexing starts at 1. In languages like Python, Javascript, and C/C++, indexing starts at 0.

@show a[1]
@show a[2]
@show a[3]
@show a[4];
a[1] = 2
a[2] = 4
a[3] = -3
a[4] = 15

You can also change the elements this way.

a[3] = -100.
@show a;
a = [2, 4, -100, 15]
@show b;
b = [2, 4, -100, 15]

Note that in Python the basic similar entity to an array is the "list". Also note that if you use NumPy in Python, as is typically the case for numerical computation, then the basic entity is called an "array". In Python the length of a list is obtained via the len function. In Julia the length of an array is obtained using length.

length(a)
4

Note that strings are kind of like arrays.

my_string = "Hello"
@show length(my_string)
@show my_string[1]
@show my_string[5];
length(my_string) = 5
my_string[1] = 'H'
my_string[5] = 'o'

But arrays are mutable (meaning you can change them) while strings are not (they are immutable).

my_string[5] = 'x'
ERROR: MethodError: no method matching setindex!(::String, ::Char, ::Int64)

The error above is how Julia tries to tell you that you can't change the string.

Arrays don't have to be arrays of just numbers, for example.

my_other_array = ["hello", 2, sqrt, 3.4, "bye", [1, 2, 3]]
6-element Vector{Any}:
  "hello"
 2
  sqrt (generic function with 19 methods)
 3.4
  "bye"
  [1, 2, 3]

Let's make a function that sums up entries of an array (assuming it is an array of numbers).

function my_sum(input_array)
    total = 0
    for i in 1:length(input_array)
        total += input_array[i]
    end
    return total
end

a = [1, 10, 100, 1000]
my_sum(a)
1111

There is also an in-built function for this.

sum(a)
1111

But you can do more things with the in-built sum. For example:

a = [-1,10,-100,1000]
@show sum(a)
@show sum(abs, a); #sum the absolute values
sum(a) = 909
sum(abs, a) = 1111

Arrays can be extended with the push! function. Note that the ! is part of the function name and indicates to us that the function modifies the array.

my_array = Int[] #empty array of integers
push!(my_array, -3)
@show my_array
push!(my_array, 10)
@show my_array;
my_array = [-3]
my_array = [-3, 10]

Let's modify our hailstone function to return an array with the sequence instead of printing the sequence.

function hailstone(n_start)
    sequence = Int[]
    n = n_start
    while n != 1
        push!(sequence, n) # was before print(n, ", ")
        if n % 2 == 0 
            n = n ÷ 2 
        else
            n = 3n + 1
        end
    end
    push!(sequence, n) # was before println(n)
    return sequence # was before return nothing
end

hailstone(7)
17-element Vector{Int64}:
  7
 22
 11
 34
 17
 52
 26
 13
 40
 20
 10
  5
 16
  8
  4
  2
  1

The Collatz Conjecture says that for every starting value the sequence eventually hits 1. Let's try to disprove it by seeing if this program gets stuck.

for n in 1:10^3
    hailstone(n)
end

It didn't get stuck (try even changing 10^3 to 10^6).

Now let's see what was the longest sequence.

length_of_longest = 0
n_of_longest = 1
for n in 1:10^3
    global length_of_longest, n_of_longest
    seq_len = length(hailstone(n))
    if seq_len > length_of_longest
        length_of_longest = seq_len
        n_of_longest = n
    end
end

println("Longest sequence is of length $length_of_longest when you start at $n_of_longest.")
Longest sequence is of length 179 when you start at 871.

One very easy way to create an array is via an array comprehension.

[i^2 for i in 1:10]
10-element Vector{Int64}:
   1
   4
   9
  16
  25
  36
  49
  64
  81
 100

The maximum function finds the maximal value of an array. We can find the 179 value from above just like this.

maximum([length(hailstone(n)) for n in 1:10^3])
179

What if we wanted to also know which index attains this maximum? See the findmax function.

findmax([length(hailstone(n)) for n in 1:10^3])
(179, 871)

Classroom Micro-Projeccts

Here are several micro-projects aimed at providing practice using the concepts above. For each of them you may need a bit more functionallity and in-built functions that what we explored so far. You can get that from web-search, LLM-help, peer help, or staff help.

Micro-Project 1: Write a little program in that gives a student a mathematics arithmetic quiz with the operations +, -, * and divide. It has input numbers in the range 1,200. Students get asked questions and need to respond with the correct answer. After 10 questions the students gets a summary of how many they got correct and what they got wrong. You may need to explore the readline and parse functions.

Micro-Project 2: Write a litte program that is a Tik-Tak-Toe game between two players. On each turn, the state of the board is re-printed. You can use 1,...,9 as input for the cells.

Micro-Project 3: Write a program that works on some fixed long string (e.g. several paragraphs of text such as the text here describing the micro projects). The program should output the character count, the word count, and perhaps other statistics about the text.

Practice questions for the practice quiz

  1. Consider the Julia code:

a = ["$i" for i in 1:3]
s = a[1]*a[2]*a[3]

What is the type and value of s?

  1. You want to write a function my_minimum that gets an array of numbers and returns the minimal value in the array. Do not use the minimum in-built function as part of your answer.

function my_minimum(a)

    return minimal_value
end

Fill in the code for the function.

  1. You want to write a Julia function num_sub_str which accepts a string main_string and a string sub_string, and returns a count of how many times sub_string is in main_string.

function num_sub_str(main_string, sub_string)

    return number_occurances
end

Fill in the code for the function.

  1. Consider the Julia function, tamid_nahon which gets two boolean values as inputs, a, and b.

function tamid_nahon(a, b)
    return !(a && b) == !a || !b
end

For what combinations of a and b is the return value true? For what is it false?

  1. The Fibbonaci sequence is 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ... (every element is the sum of the previous two and the initilization is 0, 1). Write a Julia function that computes the first n terms of the Fibbonaci sequence, returning these terms in an array.

function fibbonaci(n)
    arr = [0, 1]


    return arr
end

Fill in the code for the function.