Una Tuple è molto simile a un Vector (un array monodimensionale) sotto diversi aspetti.
Ci sono però alcune differenze importanti.
( ) invece che tra parentesi quadre [ ], anche se le parentesi si possono omettere quando il significato non è ambiguo.L'immutabilità consente più ottimizzazioni da parte del compilatore e rende le tuple più performanti degli array, quindi conviene preferirle quando la mutabilità non è necessaria.
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}
Esiste anche un costruttore Tuple(), che trasforma qualsiasi collezione iterabile in una tupla.
Utile il fatto che tratti una stringa come una collezione iterabile di caratteri.
julia> Tuple("Julia")
('J', 'u', 'l', 'i', 'a')
In qualsiasi tupla è possibile accedere agli elementi tramite indice oppure iterare su di essi.
Qualsiasi tupla puramente numerica può anche essere usata nelle funzioni matematiche come sum(), esattamente come gli array.
Una tupla può essere spacchettata nei singoli elementi usando un'assegnazione multipla.
Per gli elementi che non servono, usa l'underscore _ come variabile fittizia.
julia> tup = (1, "Julia", true)
(1, "Julia", true)
julia> num, name, _ = tup
(1, "Julia", true)
julia> uppercase(name), 2num
("JULIA", 2)
Per copiare tutti gli elementi di una tupla in un'altra tupla (o più in generale in una collezione), esiste un operatore splat ... che la spacchetta. Questo argomento sarà trattato più in dettaglio nel concetto Funzioni.
julia> new_tup = (tup..., π, '😊')
(1, "Julia", true, π, '😊')
Una Tuple contiene solo valori.
Una NamedTuple abbina ogni valore a un nome.
È poi possibile accedere ai singoli campi con la notazione del 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
Il nome è memorizzato internamente come Symbol (un tipo immutabile, e il nome è preceduto da due punti :), quindi è disponibile una sintassi alternativa:
julia> nt[:b]
7.3
A volte è utile suddividere la creazione di una tupla con nome in due passaggi:
# 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
Ci sono sorprendentemente molti altri modi per creare una tupla con nome, quindi dai un'occhiata alla documentazione se ti interessa.
È facile ottenere separatamente sia i nomi (detti anche «chiavi») sia i valori:
julia> keys(s)
(:name, :grade)
julia> values(s)
("Helen", 3)
Nota che iterando su una tupla con nome si ottengono solo i valori, mentre i nomi vengono ignorati.
Se anche i nomi servono durante un'iterazione, occorrono più passaggi.
La NamedTuple può essere convertita in un Vector di Pairs (le Pair sono l'argomento di un altro concetto).
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
La tupla con nome è quindi molto simile a un dizionario.
Tuttavia differisce da un [Dict][dict] di Julia per alcuni aspetti importanti:
Dict è mutabile; una NamedTuple, come qualsiasi tupla, è immutabile.Dict è flessibile nel tipo di chiavi che accetta; una NamedTuple può usare solo symbol come chiavi.Le tuple (con nome o no) sono un modo molto comune per trasferire dati strutturati da e verso le funzioni. In questo caso l'immutabilità è utile, perché garantisce la sicurezza dei dati e consente ottimizzazioni più aggressive da parte del compilatore.