Un Tuple est tout à fait semblable à un Vector (un array à une dimension) à bien des égards. Il présente toutefois quelques différences importantes.
( ) plutôt qu'entre crochets [ ], même si les parenthèses peuvent être omises lorsque le sens est sans ambiguïté.L'immuabilité permet davantage d'optimisations du compilateur et rend les tuples plus performants que les vecteurs, il faut donc les préférer lorsque la mutabilité n'est pas nécessaire.
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}
Il existe aussi un constructeur Tuple(), qui transforme n'importe quelle collection itérable en tuple. C'est pratique : il traite une string comme une collection itérable de caractères.
julia> Tuple("Julia")
('J', 'u', 'l', 'i', 'a')
Pour tout tuple, il est possible d'accéder aux éléments par leur indice ou de les parcourir en boucle.
Tout tuple purement numérique peut aussi être utilisé dans des fonctions mathématiques comme sum(), exactement comme les tableaux.
On peut dépaqueter un tuple en éléments individuels grâce à l'affectation multiple.
Pour les éléments dont on n'a pas besoin, on utilise le tiret bas _ comme variable fictive.
julia> tup = (1, "Julia", true)
(1, "Julia", true)
julia> num, name, _ = tup
(1, "Julia", true)
julia> uppercase(name), 2num
("JULIA", 2)
Pour copier tous les éléments d'un tuple dans un autre tuple (ou plus généralement dans une collection), il existe un opérateur splat ... qui permet de le dépaqueter. Ce point sera abordé plus en détail dans le concept Fonctions.
julia> new_tup = (tup..., π, '😊')
(1, "Julia", true, π, '😊')
Un Tuple ne contient que des valeurs.
Un NamedTuple associe chaque valeur à un nom.
On peut alors accéder aux champs individuellement avec la notation « point » :
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
Le nom est stocké en interne sous forme de Symbol (un type immuable, et le nom est préfixé par deux-points :), donc une syntaxe alternative est disponible :
julia> nt[:b]
7.3
Il est parfois utile de décomposer la création d'un tuple nommé en deux étapes :
# 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
Il existe étonnamment de nombreuses autres façons de créer un tuple nommé, alors consulte la documentation si ça t'intéresse.
Il est facile d'obtenir séparément soit les noms (aussi appelés « clés »), soit les valeurs :
julia> keys(s)
(:name, :grade)
julia> values(s)
("Helen", 3)
À noter que l'itération sur un tuple nommé ne produit que les valeurs, et les noms sont ignorés.
Si les noms sont aussi utiles lors de l'itération, il faut davantage d'étapes.
On peut convertir le NamedTuple en un Vector de Pairs (les Pair font l'objet d'un autre concept).
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
Le tuple nommé est donc assez proche d'un dictionnaire.
Il diffère toutefois d'un [Dict][dict] de Julia sur quelques points importants :
Dict est mutable ; un NamedTuple, comme tout tuple, est immuable.Dict est souple quant au type des clés qu'il accepte ; un NamedTuple ne peut utiliser que des symboles comme clés.Les tuples (nommés ou non) sont un moyen très courant de transférer des données structurées vers et depuis des fonctions. L'immuabilité est un atout dans ce cas d'usage : elle assure la sécurité des données et permet des optimisations plus agressives du compilateur.