Tuples and Named Tuples
Tuples in Julia are fixed-length collections of values, of potentially different data types, which can't be modified once initialized. Tuples are closely related to function arguments.
Initializing Tuples
Tuples can be created in a similar way to arrays, replacing square brackets with parenthesis. For example,
t = (3.14, 2.72)
Creates a variable with type Tuple{Float64, Float64}
, whose values can be accessed via t[1]
and t[2]
.
In fact, we don’t even need the parenthesis to initialize a tuple, and simply doing
t = 3.14, 2.72
will work just as well.
Tuples in general can’t be modified one initialized. For example, attempting to do:
t[1] = 2*t[1]
will result in the following ERROR: MethodError: no method matching setindex!(::Tuple{Float64, Float64}, ::Float64, ::Int64)
.
However, this doesn’t mean we can’t operate with tuples. Tuples can be copied, converted to arrays, and more.
For example, we can overwrite the value of a tuple with another tuple:
t = (2*t[1], t[2])
This also allows us to apply a broadcasting operation, like what we can do with arrays:
t = t .* 2
Converting Tuples to Arrays
Tuples can be converted to arrays in various ways: using the collect
function, array comprehensions, or the splat
notation.
a = (1, 2, 3)
t1 = collect(a);
t2 = [x for x in a];
t3 = [a...];
all producing the same output:
3-element Vector{Int64}:
1
2
3
Destructuring Tuples
We can cast the elements of a tuple as different variables in a very straightforward manner:
pi_approx, e_approx = t
Tuples are, for this reason, a convenient return type for a function that seemingly returns multiple outputs, such as we discussed in the post about functions.
Named Tuples
An interesting variant is that we can assign names to the different elements of a tuple. For example, by declaring
p = ( x = 1.1, y = 2.4)
The elements of p
can be accessed via p.x
and p.y
as well as p[1]
and p[2]
.
p
is of type NamedTuple{(:x, :y), Tuple{Float64, Float64}}
.
We can also retrieve the names of the keys and the values of a named tuple, using the keys
and values
functions, as follows:
K = keys(p) # (:x, :y)
V = values(p) # (1.1, 2.4)
and we can merge this key/value pairs into a named tuple again with the zip
function:
p_new = (; zip(K,V)...) # (x = 1.1, y = 2.4)
Merging Named Tuples
An other interesting operation we can do with Named Tuples is to merge them.
For example, let’s say that we have some parameters defined in a Named Tuple:
TemporalParams = (
Δt = 0.1,
T = 2
)
and we then compute some extra quantity which we then would like to pack together into a similar tuple, we can then do
SpatialParams = (
Δx = 0.05,
a = 0,
b = 100
)
TotalParams = merge(TemporalParams, SpatialParams)
Destructuring Named Tuples
Since Julia 1.7, there is a new sytnax that can be used to destructure a NamedTuple:
(; y, x) = p
So, let’s say that we have passed the complete list of parameters to a certain function, but we only want to use a few of them, we can then do something like
(; a,b,Δt ) = TotalParams
This will create new symbols a
,b
,and Δt
, without the need to constantly reference them like TotalParams.a
,TotalParams.b
,TotalParams.Δt
.
Also note that the parameters can be destructured regardless of their order.
This is a very cool feature I personally use a lot to deal with different kinds of parameters.