Quick Review
My plan here is to collect all the Julia code I have written so far with some notes on the techniques used. Comments should explain what each code fragment does, if it isn't obvious.
Almost all of the code on this page was done on a web site called Exercism. This is a great site for learning Julia and lots of other things too. There are some great exercises to do: the code sections below are my solutions to the exercises, often hugely improved by the great mentoring I received on the site. Strongly recommended!Hello World
It is a tradition to start with a tiny program called 'Hello World.' The simplest way to get started is in the REPL. The code: print("Hello World") is all that is needed to print the message on the screen.
If the code: println("Hello, World!") is in a file called HelloWorld.jl then it can be invoked at the command line using: julia HelloWorld.jl. The function println is used to append a line after printing the message.
The string to print could be passed in as a command line argument. In this case the arguments are stored in a Vector of Strings called ARGS. Vectors are indexed starting at 1. Hence the code in the file could be: println(ARGS[1])
However, this assumes that there will be at least one argument. Making this a little more user-friendly:
length(ARGS) > 0 ? for s in ARGS println(s) end : println("No arguments supplied")
There is more on arrays and vectors here: Arrays
A feature of Julia, called Broadcasting, is closely related to arrays and there is info on it here: Broadcasting. This page also uses an example using anonymous functions. There is more info on that subject here: Anonymous Functions.
Leap Year Test
"""
is_leap_year(year)
Return `true` if `year` is a leap year in the gregorian calendar.
"""
function is_leap_year(year)
if year % 100 == 0
return year % 400 == 0
else
return year % 4 == 0
end
end
I think that the following - which incorporates using the command line - is simpler:
function isLeapYear(year)
year % 100 == 0 ? year % 400 == 0 : year % 4 == 0
end
println(isLeapYear(parse(Int64, ARGS[1])) ? "True" : "False")
Clock
#=
This is my Clock code after getting brilliant feed back from Ian Weaver
=#
using Dates, Printf
# Clock
# Just use minutes internally.
struct Clock
minutes::Int
Clock(minutes) = new(mod(minutes, 60 * 24))
end
# Public interface
Clock(hour, min) = Clock(hour * 60 + min)
Base.:+(c::Clock, m::Minute) = Clock(c.minutes + m.value)
Base.:-(c::Clock, m::Minute) = c + -m
function Base.show(io::IO, c::Clock)
(h,m) = divrem(c.minutes, 60)
str = @sprintf "%02i:%02i" h m
show(io, str)
end
There are some important learning points here. The use of "Base."" is to extend a method in the standard 'Base' library to work on a newly defined type, in this case Clock. So Base.show extends the show method in the Base library to display a nicely formatted clock time.
The public interface also extends the Base definition of + and - to work with the Clock struct.
Note the use of : to quote the function names + and -.
There are situations where the function name will need to be enclosed in brackets e.g:
Base.:(==)(a::MyType, b::MyType) = .....
There is an example here:
Operator Overloading
Raindrops
Convert a number into its corresponding raindrop sounds. If a given number: is divisible by 3, add "Pling" to the result. is divisible by 5, add "Plang" to the result. is divisible by 7, add "Plong" to the result. is not divisible by 3, 5, or 7, the result should be the number as a string.
function raindrops(n)
d = [(3,"Pling"), (5,"Plang"), (7, "Plong")]
s = [v for (k, v) in d if n % k == 0] |> join
s == "" ? string(n) : s
end
Pangram
"""
ispangram(input)
This was my mentor's solution (Ian Weaver)
It is an example of a single line function.
The alphabetic range is captured in the 'a':'z' part.
The subset symbol can be typed \subseteq + tab in the REPL.
The conversion to lowercase is a library function.
"""
ispangram(input::AbstractString) = 'a':'z' ⊆ lowercase(input)
Difference of Squares
#=
Find the difference between the square of the sum and the sum of the squares of the first N natural numbers.
=#
function square_of_sum(n)
square(n) = n * n
square(n*(n+1)) ÷ 4
end
function sum_of_squares(n)
(n*(n+1)*(2n+1)) ÷ 6
end
function difference(n)
square_of_sum(n) - sum_of_squares(n)
end
Nucleotide Count
"""
count_nucleotides(strand)
The count of each nucleotide within `strand` as a dictionary.
Invalid strands raise a `DomainError`.
"""
function count_nucleotides(strand)
dict = Dict{Char, Integer}('A' => 0, 'C' => 0, 'G' => 0, 'T' => 0)
for k in strand
try
dict[k] += 1
catch e
throw(DomainError("Invalid DNA string"))
end
end
return dict
end
Hamming
#=
Given two equal length strings return the number of differences.
Throw error if the strings are unequal.
=#
function distance(a,b)
if length(a) != length(b)
throw(ArgumentError("distance($a, $b)"))
end
count_not_equal((f,s)) = f != s ? 1 : 0
sum(count_not_equal, zip(a, b); init = 0)
end
Rotational Cipher
#=
This is the second implementation of the Caesar cipher.
Next steps are to figure out how to use string literals and meta-programming.
=#
function rotate(offset :: Int, key :: Int, c :: Char)
convert(Char,(((convert(Int, c) - offset) + key) % 26) + offset)
end
function rotate(key :: Int, c :: Char)
if c in 'A':'Z'
rotate(65, key, c)
elseif c in 'a':'z'
rotate(97, key, c)
else
return c
end
end
function rotate(key :: Int, text :: String)
rotateN(c) = rotate(key, c)
map(rotateN, text)
end
macro R13_str(text)
# Encrypt a single character, given offset.
function rotate1(offset :: Int, c :: Char)
key = 13
convert(Char,(((convert(Int, c) - offset) + key) % 26) + offset)
end
# Encrypt a single character, works for upper and lower case.
function rotate2(c :: Char)
if c in 'A':'Z'
rotate1(65, c)
elseif c in 'a':'z'
rotate1(97, c)
else
return c
end
end
# Encrypt a string
function rotate3(s :: String)
map(rotate2, s)
end
rotate3(text)
end
Secret Handshake
Instructions Your task is to convert a number between 1 and 31 to a sequence of actions in the secret handshake. The sequence of actions is chosen by looking at the rightmost five digits of the number once it's been converted to binary. Start at the right-most digit and move left. The actions for each number place are: 00001 = wink 00010 = double blink 00100 = close your eyes 01000 = jump 10000 = Reverse the order of the operations in the secret handshake. Let's use the number 9 as an example: 9 in binary is 1001. The digit that is farthest to the right is 1, so the first action is wink. Going left, the next digit is 0, so there is no double-blink. Going left again, the next digit is 0, so you leave your eyes open. Going left again, the next digit is 1, so you jump. That was the last digit, so the final code is: wink, jump Given the number 26, which is 11010 in binary, we get the following actions: double blink jump reverse actions The secret handshake for 26 is therefore: jump, double blink
function secret_handshake(code)
secrets = [(1, "wink"), (2, "double blink"), (4, "close your eyes"), (8, "jump")]
r = [v for (k, v) in secrets if k & code == k]
code & 16 == 16 ? reverse(r) : r
end
Killer Sudoku Helper
# ===================================================================================
# Killer sudoko
# combinations_in_cage(sum, num_cells)
# Inputs: sum is the total to arrive at, num_cells is the number of cells in the cage
# Optional paramter: an array of digits which must not be in the final output.
# Return: array of arrays
# Structure of code at present is that an array implements a switch.
# Then each size of cage creates the list for a given sum.
# ============================================================================
# List the cases to deal with:
# cases[number of cells] = function to compute possible ways of getting sum.
# Then evaluate the function using the parameters
function combinations_in_cage(sum, num_cells)
cases = [f1, f2, f3, f4, f5, f6, f7, f8, f9]
sort(cases[num_cells](sum))
end
# Exclude the arrays containing any of the unwanted numbers
function combinations_in_cage(sum, num_cells, unwanted)
exclude(vals) = !(any(in(unwanted), vals))
filter(exclude, combinations_in_cage(sum, num_cells))
end
# These are the cases used in the function
# combinations_in_cage(sum, cells)
# 1-digit cages, output has to be the same as the sum
f1(sum) = [[sum]]
# cages done using list comprehension
f2(sum) = [[a,b] for a in 1:8, b in 2:9 if a<b && sum == (a+b) ]
f3(sum) = [[a,b,c] for a in 1:7, b in 2:8, c in 3:9 if a<b<c && sum == (a+b+c) ]
f4(sum) = [[a,b,c,d] for a in 1:6, b in 2:7, c in 3:8, d in 4:9 if a<b<c<d && sum == (a+b+c+d) ]
f5(sum) = [[a,b,c,d,e] for a in 1:5, b in 2:6, c in 3:7, d in 4:8, e in 5:9 if a<b<c<d<e && sum == (a+b+c+d+e) ]
# Same thing but differently laid out to keep lines from getting too long.
function f6(sum)
[[a,b,c,d,e,f] for a in 1:4, b in 2:5, c in 3:6, d in 4:7, e in 5:8, f in 6:9
if a<b<c<d<e<f && sum == (a+b+c+d+e+f)]
end
function f7(sum)
[[a,b,c,d,e,f,g] for a in 1:3, b in 2:4, c in 3:5, d in 4:6, e in 5:7, f in 6:8, g in 7:9
if a<b<c<d<e<f<g && sum == (a+b+c+d+e+f+g)]
end
function f8(sum)
[[a,b,c,d,e,f,g, h] for a in 1:2, b in 2:3, c in 3:4, d in 4:5, e in 5:6, f in 6:7, g in 7:8, h in 8:9
if a<b<c<d<e<f<g<h && sum == (a+b+c+d+e+f+g+h)]
end
# f9 only has one useful output
f9(sum) = sum == 45 ? [[1,2,3,4,5,6,7,8,9]] : [[]]
Multi-dimensional matrices
In the last example we saw how a two dimensional matrix can be constructed. A simple example would be:
tables = [a * b for a in 1:12, b in 1:12]
Which gives the output:
12x12 Matrix{Int64}:
1 2 3 4 5 6 7 8 9 10 11 12
2 4 6 8 10 12 14 16 18 20 22 24
3 6 9 12 15 18 21 24 27 30 33 36
4 8 12 16 20 24 28 32 36 40 44 48
5 10 15 20 25 30 35 40 45 50 55 60
6 12 18 24 30 36 42 48 54 60 66 72
7 14 21 28 35 42 49 56 63 70 77 84
8 16 24 32 40 48 56 64 72 80 88 96
9 18 27 36 45 54 63 72 81 90 99 108
10 20 30 40 50 60 70 80 90 100 110 120
11 22 33 44 55 66 77 88 99 110 121 132
12 24 36 48 60 72 84 96 108 120 132 144