Julia Language
Le tuple
Ricerca…
Sintassi
- un,
- a, b
- a, b = xs
- ()
- (un,)
- (a, b)
- (a, b ...)
- Tupla {T, U, V}
- NTuple {N, T}
- Tupla {T, U, Vararg {V}}
Osservazioni
Le tuple hanno prestazioni di runtime molto migliori degli array per due motivi: i loro tipi sono più precisi e la loro immutabilità consente loro di essere allocati nello stack anziché nell'heap. Tuttavia, questa digitazione più precisa viene fornita con un overhead in più tempo di compilazione e maggiori difficoltà nel raggiungere la stabilità del tipo .
Introduzione a Tuples
Tuple
sono collezioni ordinate immutabili di oggetti distinti arbitrari, dello stesso tipo o di tipi diversi. Tipicamente, le tuple sono costruite usando la sintassi (x, y)
.
julia> tup = (1, 1.0, "Hello, World!")
(1,1.0,"Hello, World!")
I singoli oggetti di una tupla possono essere recuperati usando la sintassi dell'indicizzazione:
julia> tup[1]
1
julia> tup[2]
1.0
julia> tup[3]
"Hello, World!"
Implementano l' interfaccia iterabile e possono quindi essere iterati utilizzando loop for
:
julia> for item in tup
println(item)
end
1
1.0
Hello, World!
Le tuple supportano anche una varietà di funzioni di collezioni generiche, come il reverse
o la length
:
julia> reverse(tup)
("Hello, World!",1.0,1)
julia> length(tup)
3
Inoltre, le tuple supportano una varietà di operazioni di raccolta di ordine superiore , incluse any
, all
, map
o broadcast
:
julia> map(typeof, tup)
(Int64,Float64,String)
julia> all(x -> x < 2, (1, 2, 3))
false
julia> all(x -> x < 4, (1, 2, 3))
true
julia> any(x -> x < 2, (1, 2, 3))
true
La tupla vuota può essere costruita usando ()
:
julia> ()
()
julia> isempty(ans)
true
Tuttavia, per costruire una tupla di un elemento, è necessaria una virgola finale. Questo perché le parentesi ( (
e )
) sarebbero altrimenti trattate come operazioni di raggruppamento insieme invece di costruire una tupla.
julia> (1)
1
julia> (1,)
(1,)
Per coerenza, una virgola finale è anche consentita per le tuple con più di un elemento.
julia> (1, 2, 3,)
(1,2,3)
Tipi di tupla
Il typeof
una tupla è un sottotipo di Tuple
:
julia> typeof((1, 2, 3))
Tuple{Int64,Int64,Int64}
julia> typeof((1.0, :x, (1, 2)))
Tuple{Float64,Symbol,Tuple{Int64,Int64}}
A differenza di altri tipi di dati, i tipi di Tuple
sono covarianti . Altri tipi di dati in Julia sono generalmente invarianti. Così,
julia> Tuple{Int, Int} <: Tuple{Number, Number}
true
julia> Vector{Int} <: Vector{Number}
false
Questo è il caso perché ovunque sia accettata una Tuple{Number, Number}
, così anche una Tuple{Int, Int}
, poiché ha anche due elementi, entrambi sono numeri. Questo non è il caso di un Vector{Int}
contro un Vector{Number}
, in quanto una funzione che accetta un Vector{Number}
può desiderare di memorizzare un punto mobile (es. 1.0
) o un numero complesso (ad esempio 1+3im
) in tale un vettore
La covarianza dei tipi di tupla significa che Tuple{Number}
(a differenza di Vector{Number}
) è in realtà un tipo astratto:
julia> isleaftype(Tuple{Number})
false
julia> isleaftype(Vector{Number})
true
I sottotipi concreti di Tuple{Number}
includono Tuple{Int}
, Tuple{Float64}
, Tuple{Rational{BigInt}}
e così via.
Tuple
tipi di Vararg
possono contenere un Vararg
terminazione come ultimo parametro per indicare un numero indefinito di oggetti. Ad esempio, Tuple{Vararg{Int}}
è il tipo di tutte le tuple che contengono un numero qualsiasi di Int
s, possibilmente zero:
julia> isa((), Tuple{Vararg{Int}})
true
julia> isa((1,), Tuple{Vararg{Int}})
true
julia> isa((1,2,3,4,5), Tuple{Vararg{Int}})
true
julia> isa((1.0,), Tuple{Vararg{Int}})
false
mentre Tuple{String, Vararg{Int}}
accetta tuple costituite da una stringa , seguite da qualsiasi numero (possibilmente zero) di Int
s.
julia> isa(("x", 1, 2), Tuple{String, Vararg{Int}})
true
julia> isa((1, 2), Tuple{String, Vararg{Int}})
false
Combinato con co-varianza, ciò significa che Tuple{Vararg{Any}}
descrive qualsiasi tupla. In effetti, Tuple{Vararg{Any}}
è solo un altro modo di dire Tuple
:
julia> Tuple{Vararg{Any}} == Tuple
true
Vararg
accetta un secondo parametro di tipo numerico che indica quante volte dovrebbe verificarsi esattamente il suo primo parametro di tipo. (Per default, se non specificato, questo secondo parametro tipo è un typevar che può assumere qualsiasi valore, motivo per cui un numero qualsiasi di Int
s sono accettato nella Vararg
s sopra.) Tuple
tipi terminanti in un determinato Vararg
verrà automaticamente esteso al numero richiesto di elementi:
julia> Tuple{String,Vararg{Int, 3}}
Tuple{String,Int64,Int64,Int64}
Esiste una notazione per tuple omogenee con un Vararg
specificato: NTuple{N, T}
. In questa notazione, N
indica il numero di elementi nella tupla e T
indica il tipo accettato. Per esempio,
julia> NTuple{3, Int}
Tuple{Int64,Int64,Int64}
julia> NTuple{10, Int}
NTuple{10,Int64}
julia> ans.types
svec(Int64,Int64,Int64,Int64,Int64,Int64,Int64,Int64,Int64,Int64)
Nota che NTuple
s oltre una certa dimensione viene mostrato semplicemente come NTuple{N, T}
, invece del modulo Tuple
espanso, ma sono sempre dello stesso tipo:
julia> Tuple{Int,Int,Int,Int,Int,Int,Int,Int,Int,Int}
NTuple{10,Int64}
Dispacciamento di tipi di tuple
Poiché gli elenchi dei parametri della funzione di Julia sono essi stessi delle tuple, l' invio di vari tipi di tuple è spesso più semplice attraverso i parametri del metodo stessi, spesso con un uso liberale per l'operatore "splatting" ...
Ad esempio, considera l'implementazione del reverse
per le tuple, da Base
:
revargs() = ()
revargs(x, r...) = (revargs(r...)..., x)
reverse(t::Tuple) = revargs(t...)
L'implementazione dei metodi sulle tuple in questo modo preserva la stabilità del tipo , che è fondamentale per le prestazioni. Possiamo vedere che non c'è nessun overhead a questo approccio usando la macro @code_warntype
:
julia> @code_warntype reverse((1, 2, 3))
Variables:
#self#::Base.#reverse
t::Tuple{Int64,Int64,Int64}
Body:
begin
SSAValue(1) = (Core.getfield)(t::Tuple{Int64,Int64,Int64},2)::Int64
SSAValue(2) = (Core.getfield)(t::Tuple{Int64,Int64,Int64},3)::Int64
return (Core.tuple)(SSAValue(2),SSAValue(1),(Core.getfield)(t::Tuple{Int64,Int64,Int64},1)::Int64)::Tuple{Int64,Int64,Int64}
end::Tuple{Int64,Int64,Int64}
Anche se un po 'difficile da leggere, il codice qui sta semplicemente creando una nuova tupla con valori 3 °, 2 ° e 1 ° elementi della tupla originale, rispettivamente. Su molte macchine, questo compila verso il codice LLVM estremamente efficiente, che consiste in carichi e negozi.
julia> @code_llvm reverse((1, 2, 3))
define void @julia_reverse_71456([3 x i64]* noalias sret, [3 x i64]*) #0 {
top:
%2 = getelementptr inbounds [3 x i64], [3 x i64]* %1, i64 0, i64 1
%3 = getelementptr inbounds [3 x i64], [3 x i64]* %1, i64 0, i64 2
%4 = load i64, i64* %3, align 1
%5 = load i64, i64* %2, align 1
%6 = getelementptr inbounds [3 x i64], [3 x i64]* %1, i64 0, i64 0
%7 = load i64, i64* %6, align 1
%.sroa.0.0..sroa_idx = getelementptr inbounds [3 x i64], [3 x i64]* %0, i64 0, i64 0
store i64 %4, i64* %.sroa.0.0..sroa_idx, align 8
%.sroa.2.0..sroa_idx1 = getelementptr inbounds [3 x i64], [3 x i64]* %0, i64 0, i64 1
store i64 %5, i64* %.sroa.2.0..sroa_idx1, align 8
%.sroa.3.0..sroa_idx2 = getelementptr inbounds [3 x i64], [3 x i64]* %0, i64 0, i64 2
store i64 %7, i64* %.sroa.3.0..sroa_idx2, align 8
ret void
}
Più valori di ritorno
Le tuple sono spesso utilizzate per valori di ritorno multipli. Gran parte della libreria standard, incluse due delle funzioni dell'interfaccia iterabile ( next
e done
), restituisce tuple contenenti due valori correlati ma distinti.
Le parentesi attorno alle tuple possono essere omesse in determinate situazioni, rendendo più facili da implementare più valori di ritorno. Ad esempio, possiamo creare una funzione per restituire sia radici quadrate positive che negative di un numero reale:
julia> pmsqrt(x::Real) = sqrt(x), -sqrt(x)
pmsqrt (generic function with 1 method)
julia> pmsqrt(4)
(2.0,-2.0)
L'assegnazione della distruzione può essere utilizzata per decomprimere i valori di ritorno multipli. Per memorizzare le radici quadrate nelle variabili a
e b
, è sufficiente scrivere:
julia> a, b = pmsqrt(9.0)
(3.0,-3.0)
julia> a
3.0
julia> b
-3.0
Un altro esempio di ciò sono le funzioni divrem
e fldmod
, che eseguono contemporaneamente una divisione intera (troncata o pavimentata) e un'operazione resto:
julia> q, r = divrem(10, 3)
(3,1)
julia> q
3
julia> r
1