Tricks in Julia

Usefull tricks in Julia for dealing with numbers, strings, dates and more.

After doing various small Julia projects I’ve had to learn a number of tricks or solutions to small problems, that I think would be usefull to collect:

Parsing Strings

Extract 3 numbers from a string into 3 variables:

a, b, c = parse.(Int, split("11 12 13"))

This requires some explanation. parse(Int, s) will parse a single string s as a number. However when you call parse.(Int, ["11", "12", "13"]), the dot causes parse to be called for every element of the array.

Initialization

There are some gotchas with Julia initialization if you come from C/C++ background although this should not be so odd to Python or Ruby developers.

julia> mutable struct Point
         x::Int
         y::Int
       end

julia> points = fill(Point(0, 0), 4)
    4-element Array{Point,1}:
     Point(0,0)
     Point(0,0)
     Point(0,0)
     Point(0,0)

That looks all fine, until you make a change and get this:

julia> points[2].x = 20
julia> points
4-element Array{Point,1}:
 Point(20,0)
 Point(20,0)
 Point(20,0)
 Point(20,0)

That was probably not as expected. The reason for this is that references to the created Point object in fill is copied not the values themselves. So what we actually got to do is:

points = [Point(0, 0) for _ in 1:4]

Integers

There are a lot of issues with just dealing with something simple as integers. First of all how do yo get the min and max values of the integers you are using:

julia> typemax(Int8)
127

julia> typemax(UInt8)
0xff # which is 255 decimal

julia> typemin(Int8)
-128

One also has to remember that Julia integers do not get promoted to larger values when overflowing. So e.g. taking the factorial of a really big number like 100 requires using BigInt:

 julia> factorial(BigInt(25))
 15511210043330985984000000

Division

One thing I got confused by being more used to C/C++ is how division works in Julia. There are several ways of doing it depending on what you want. Using integers in division does not produce integers automatically. The default is to give floating point answer:

julia> 10 / 6
1.6666666666666667

Which has of course the minor inaccuracies of floating point arithmetic. If you want accurate results you can compute fractions with the // division operator:

julia> a = 10 // 6
5//3

julia> numerator(a)
5

julia> denominator(a)
3

If you want to work with C/C++ style division then we use the div, rem and mod functions:

julia> div(10, 6)
1

> rem(10, 6)
4

C/C++ style remainder

julia> 10 % 6
4

Modulus is not quite the same as remaineder. E.g.

julia> rem(-21, 4)
-1

julia> mod(-21, 4)
3

With modulus we can imagine doing calculations on a clock.

Reading files

Julia has some nice ways you can compose functions to read files.

lines = open(readlines, "myfile.txt")

This corresponds to writing:

f = open("myfile.txt")
lines = readlines(f)
close(f)

With the julia do end syntax sugar you can use this to conveniently read a file and not forget to close it:

open("myfile.txt") do f
   println(readline(f))     # print out first line in file
end                         # file stream automatically closes here

Working with dates

This is just to show how to deal with a common case:

julia> using Dates

julia> (date, time) = split("5/8/2014 0:16:03")
2-element Array{SubString{ASCIIString},1}:
 "5/8/2014"
 "0:16:03" 

Then as shown before we can unpack an array into separate variables like this:

julia> y, m, d = reverse(parse.(Int, split(date, '/')))
3-element Array{Int64,1}:
 2014
    8
    5

julia> date = Date(y, m, d)
2014-08-05

You could also do this in one go by unpacking an array into function arguments using ...:

julia> Date(reverse(parse.(Int, split(date, '/')))...)
2014-08-05

Enumeration

Regular enumeration in Julia is straightforward but it might not be obvious how you enumerate over both values and keys or indicies in an array or dictionary:

> for (index, value) in enumerate(["one", "two", "three"])
           println(index, ": ", value)
       end
1: one
2: two
3: three

Accessing fields of a type

The syntax for accessing fields known at compile time is similar in Julia as in most other languages with Algol syntax:

struct FooBar
	foo::Int64
	bar::String
end
julia> a = FooBar(42, "The answer to everything")

julia> a.bar
"The answer to everything"

julia> a.foo
42

However accessing fields not known at compile time is different. Imagine the user inputs the name of a field to access or the names of fields are read of a file. Unlike Ruby and Python, Julia’s objects are not implemented as hashtables. So accessing fields given a name is a bit different:

julia> getfield(a, :bar)
"The answer to everything"

If you only got the name of a field as a string you can do:

julia> getfield(a, Symbol("bar"))
"The answer to everything"

To find out what fields actually exist you can use names:

julia> fieldnames(FooBar)
(:foo, :bar)

Alternatively if you got an instance you can see both the name of the fields and their values with dump:

julia> dump(a)
FooBar
  foo: Int64 42
  bar: String "The answer to everything"

Most of these code snippets or approaches I picked up from doing small things like implement an algorithm in project Euler. I hope to write some bigger piece of code and see how Julia stacks up and how well suited Julia is for more software engineering type of challenges.

comments powered by Disqus