Una Tuple es bastante similar a un Vector (un array unidimensional) en muchos aspectos.
Sin embargo, hay algunas diferencias importantes.
( ) en lugar de corchetes [ ], aunque los paréntesis se pueden omitir cuando el significado no es ambiguo.La inmutabilidad permite más optimizaciones del compilador y hace que las tuplas tengan mejor rendimiento que los vectores, así que conviene preferirlas cuando la mutabilidad no sea necesaria.
julia> t = (3, 5.2, "xyz")
(3, 5.2, "xyz")
# Non-homogeneous:
julia> dump(t)
Tuple{Int64, Float64, String}
1: Int64 3
2: Float64 5.2
3: String "xyz"
julia> t[2]
5.2
# Immutable:
julia> t[2] = 7.3
ERROR: MethodError: no method matching setindex!(::Tuple{Int64, Float64, String}
También existe un constructor Tuple(), que convierte cualquier colección iterable en una tupla.
De forma muy útil, esto trata a un string como una colección iterable de caracteres.
julia> Tuple("Julia")
('J', 'u', 'l', 'i', 'a')
En cualquier tupla, es posible acceder a los elementos por índice o recorrerlos con un bucle.
Cualquier tupla puramente numérica también se puede usar en funciones matemáticas como sum(), exactamente igual que los arrays.
Una tupla se puede desempaquetar en elementos individuales usando asignación múltiple.
Para los elementos que no necesites, usa el guion bajo _ como variable ficticia.
julia> tup = (1, "Julia", true)
(1, "Julia", true)
julia> num, name, _ = tup
(1, "Julia", true)
julia> uppercase(name), 2num
("JULIA", 2)
Para copiar todos los elementos de una tupla a otra tupla (o, más en general, a una colección), existe un operador splat ... que la desempaqueta.
Esto se tratará con más detalle en el concepto de Funciones.
julia> new_tup = (tup..., π, '😊')
(1, "Julia", true, π, '😊')
Una Tuple solo contiene valores.
Una NamedTuple asocia cada valor con un nombre.
Luego se puede acceder a cada campo individual con la notación de «punto»:
julia> nt = (a = 1, b = 7.3)
(a = 1, b = 7.3)
julia> dump(nt)
@NamedTuple{a::Int64, b::Float64}
a: Int64 1
b: Float64 7.3
julia> nt.b
7.3
El nombre se guarda internamente como un Symbol (un tipo inmutable, y el nombre lleva el prefijo de dos puntos :), así que hay una sintaxis alternativa disponible:
julia> nt[:b]
7.3
A veces resulta útil dividir la creación de una tupla con nombre en dos pasos:
# Step 1
julia> Student = NamedTuple{(:name, :grade), Tuple{String, Int}}
@NamedTuple{name::String, grade::Int64}
julia> typeof(Student)
DataType
# Step 2
julia> s = Student( ("Helen", 3) )
(name = "Helen", grade = 3)
julia> dump(s)
@NamedTuple{name::String, grade::Int64}
name: String "Helen"
grade: Int64 3
Hay sorprendentemente muchas otras formas de crear una tupla con nombre, así que consulta la documentación si te interesa.
Es fácil obtener por separado los nombres (también llamados «claves») o los valores:
julia> keys(s)
(:name, :grade)
julia> values(s)
("Helen", 3)
Ten en cuenta que la iteración sobre una tupla con nombre solo produce los valores, y los nombres se ignoran.
Si los nombres también te resultan útiles en una iteración, hacen falta más pasos.
La NamedTuple se puede convertir en un Vector de Pairs (los Pair son el tema de otro concepto).
julia> pairs(s)
pairs(::NamedTuple) with 2 entries:
:name => "Helen"
:grade => 3
julia> collect(pairs(s))
2-element Vector{Pair{Symbol, Any}}:
:name => "Helen"
:grade => 3
Así, la tupla con nombre se parece bastante a un diccionario.
Sin embargo, se diferencia de un [Dict][dict] de Julia en algunos aspectos importantes:
Dict es mutable; una NamedTuple, como cualquier tupla, es inmutable.Dict es flexible en el tipo de claves que acepta; una NamedTuple solo puede usar símbolos como claves.Las tuplas (con nombre o sin él) son una forma muy común de transferir datos estructurados desde y hacia las funciones. La inmutabilidad es buena en este caso de uso, ya que brinda seguridad de los datos y permite optimizaciones del compilador más agresivas.