Programming in JuliaCustom types
Suppose you want to write a program which keeps track of the albums you own. Each album is associated with several data, like the name of the album, the year it came out, the number of tracks, etc. You could store all these data by assigning them to different variables, but that becomes untidy very quickly. For example, you will frequently want to pass an album to a function, and you don't want that function to require a long list of parameters just because the album has a lot of data associated with it.
What you want is to be able to treat each album as its own Julia object, with all its associated data stored inside. In other words, you want an Album
type. You can do that with the struct
keyword.
struct Album name artist year duration end A = Album("Abbey Road", "The Beatles", 1969, "47:23")
In the last line, we have defined a new object of type Album
and saved it to the variable A
. We call name
, artist
, year
, and duration
fields of the Album
type. The fields of an object can be accessed by name using dot syntax:
A.duration
We can define functions to operate on our new data type. For example, we might want to be able to calculate how old an album was as of a given year. We can specify types for a function's arguments using double colon syntax:
function num_years_ago(A::Album, year::Integer) year - A.year end
Note: Integer
is an abstract type which encompasses Int64
, Int32
(which uses 32 bits instead of 64), and any other type which represents a mathematical integer.
One reason it's helpful to be able to specify type information when defining a function is that we can specify different behavior for different types:
function num_years_ago(earlier_year::Integer, later_year::Integer) later_year - earlier_year end num_years_ago(A, 2019) # returns 50 num_years_ago(1986, 2019) # returns 33
We say that num_years_ago
now has two methods: one which accepts an Album
as its first argument and an Integer
as its second argument, and one which accepts Integer
s for both arguments. Julia is responsible for correctly dispatching each function call to the correct method. This feature of Julia is called multiple dispatch.
Exercise
Write a type Line
for representing non-vertical lines in the plane. Write a two-argument method intersect
which finds the intersection point of two lines (you may return the intersection point as a tuple of floats, and for simplicity, you can assume the lines intersect).
Solution. Since we only need to store non-vertical lines, we can represent every line via its slope and intercept. We can specify that these data types should be Float64
s if we want:
struct Line slope::Float64 intercept::Float64 end
The intersection point of two lines is given by
function intersect(L::Line,M::Line) x = -(L.intercept-M.intercept)/(L.slope-M.slope) y = L.intercept + x*L.slope (x,y) end