Julia Language
Tuples
Suche…
Syntax
- ein,
- a, b
- a, b = xs
- ()
- (ein,)
- (a, b)
- (a, b ...)
- Tupel {T, U, V}
- NTuple {N, T}
- Tupel {T, U, Vararg {V}}
Bemerkungen
Tupel haben aus zwei Gründen eine viel bessere Laufzeitperformance als Arrays : Ihre Typen sind präziser und aufgrund ihrer Unveränderlichkeit können sie auf dem Stapel statt auf dem Heap zugewiesen werden. Diese genauere Typisierung bringt jedoch mehr Overhead bei der Kompilierung mit sich und es ist schwieriger, die Typstabilität zu erreichen .
Einführung in Tuples
Tuple
sind unveränderliche geordnete Sammlungen von beliebigen Objekten, entweder vom selben Typ oder von verschiedenen Typen . Typischerweise werden Tupel unter Verwendung der (x, y)
-Syntax konstruiert.
julia> tup = (1, 1.0, "Hello, World!")
(1,1.0,"Hello, World!")
Die einzelnen Objekte eines Tupels können mit der Indexierungssyntax abgerufen werden:
julia> tup[1]
1
julia> tup[2]
1.0
julia> tup[3]
"Hello, World!"
Sie implementieren die iterierbare Schnittstelle und können daher mithilfe von for
Schleifen iteriert werden :
julia> for item in tup
println(item)
end
1
1.0
Hello, World!
Tupel unterstützen auch eine Reihe generischer Sammlungsfunktionen, z. B. reverse
oder length
:
julia> reverse(tup)
("Hello, World!",1.0,1)
julia> length(tup)
3
Darüber hinaus unterstützen Tupel eine Reihe von Erfassungsvorgängen höherer Ordnung , darunter any
, all
map
oder 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
Das leere Tupel kann mit ()
:
julia> ()
()
julia> isempty(ans)
true
Um ein Tupel eines Elements zu erstellen, ist jedoch ein nachfolgendes Komma erforderlich. Dies liegt daran, dass die Klammern ( (
und )
) sonst als Gruppieren von Operationen behandelt würden, anstatt ein Tupel zu erstellen.
julia> (1)
1
julia> (1,)
(1,)
Aus Gründen der Konsistenz ist ein Nachkomma auch für Tupel mit mehr als einem Element zulässig.
julia> (1, 2, 3,)
(1,2,3)
Tuple-Typen
Der typeof
eines Tupels ist ein Untertyp von Tuple
:
julia> typeof((1, 2, 3))
Tuple{Int64,Int64,Int64}
julia> typeof((1.0, :x, (1, 2)))
Tuple{Float64,Symbol,Tuple{Int64,Int64}}
Im Gegensatz zu anderen Datentypen sind Tuple
Typen kovariant . Andere Datentypen in Julia sind im Allgemeinen unveränderlich. Somit,
julia> Tuple{Int, Int} <: Tuple{Number, Number}
true
julia> Vector{Int} <: Vector{Number}
false
Dies ist der Fall, da überall ein Tuple{Number, Number}
akzeptiert wird, ebenso ein Tuple{Int, Int}
, da er auch zwei Elemente hat, die beide Zahlen sind. Dies ist bei einem Vector{Int}
gegenüber einem Vector{Number}
nicht der Fall, da eine Funktion, die einen Vector{Number}
akzeptiert, möglicherweise einen Gleitkommawert (z. B. 1.0
) oder eine komplexe Zahl (z. B. 1+3im
) in einem solchen 1+3im
ein Vektor.
Die Kovarianz von Tupeltypen bedeutet, dass Tuple Tuple{Number}
(wiederum im Gegensatz zu Vector{Number}
) tatsächlich ein abstrakter Typ ist:
julia> isleaftype(Tuple{Number})
false
julia> isleaftype(Vector{Number})
true
Zu den konkreten Subtypen von Tuple{Number}
gehören Tuple{Int}
, Tuple{Float64}
, Tuple{Rational{BigInt}}
und so weiter.
Tuple
Typen können einen abschließenden Vararg
als letzten Parameter enthalten, um eine unbegrenzte Anzahl von Objekten anzuzeigen. Tuple{Vararg{Int}}
ist beispielsweise der Typ aller Tupel, die eine beliebige Anzahl von Int
s enthalten, möglicherweise Null:
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
Tuple{String, Vararg{Int}}
akzeptiert Tupel, die aus einem String bestehen , gefolgt von einer beliebigen Anzahl (möglicherweise Null) von Int
s.
julia> isa(("x", 1, 2), Tuple{String, Vararg{Int}})
true
julia> isa((1, 2), Tuple{String, Vararg{Int}})
false
Kombiniert mit Tuple{Vararg{Any}}
bedeutet dies, dass Tuple{Vararg{Any}}
jedes Tupel beschreibt. In der Tat ist Tuple{Vararg{Any}}
nur eine andere Art, Tuple
sagen:
julia> Tuple{Vararg{Any}} == Tuple
true
Vararg
akzeptiert einen zweiten numerischen Typparameter, der angibt, wie oft genau der erste Typparameter vorkommen soll. (Standardmäßig , wenn nicht spezifiziert, wobei diese zweite Typ Parameter ist ein typevar , die jeden Wert annehmen kann, weshalb eine beliebige Anzahl von Int
s in der akzeptiert Vararg
oben s.) Tuple
Arten in einer bestimmten Endung Vararg
wird automatisch auf die erweitert werden gewünschte Anzahl von Elementen:
julia> Tuple{String,Vararg{Int, 3}}
Tuple{String,Int64,Int64,Int64}
Notation existiert für homogene Tupel mit einem angegebenen Vararg
: NTuple{N, T}
. In dieser Notation bezeichnet N
die Anzahl der Elemente im Tupel und T
den akzeptierten Typ. Zum Beispiel,
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)
Beachten Sie, dass NTuple
jenseits einer bestimmten Größe anstelle des erweiterten Tuple
Formulars einfach als NTuple{N, T}
, sie sind jedoch immer noch derselbe Typ:
julia> Tuple{Int,Int,Int,Int,Int,Int,Int,Int,Int,Int}
NTuple{10,Int64}
Versand auf Tupel-Typen
Da Julia-Funktionsparameterlisten selbst Tupel sind, ist das Dispatching auf verschiedene Arten von Tupeln oft einfacher über die Methodenparameter selbst, oft mit freiem Gebrauch für den Operator "Splatting" ...
Betrachten Sie beispielsweise die Implementierung von reverse
für Tupel von Base
:
revargs() = ()
revargs(x, r...) = (revargs(r...)..., x)
reverse(t::Tuple) = revargs(t...)
Die Implementierung Methoden auf Tupeln bewahrt auf diese Weise Stabilität geben , die für die Leistung entscheidend ist. Wir können sehen, dass dieser Ansatz mit dem @code_warntype
Makro nicht @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}
Obwohl es etwas schwer zu lesen ist, erstellt der Code einfach ein neues Tupel mit den Werten 3., 2. bzw. 1. Element des ursprünglichen Tupels. Bei vielen Maschinen wird dies zu äußerst effizientem LLVM-Code, der aus Ladungen und Speichern besteht.
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
}
Mehrere Rückgabewerte
Tupel werden häufig für mehrere Rückgabewerte verwendet. Ein Großteil der Standardbibliothek, einschließlich zweier Funktionen der iterierbaren Schnittstelle ( next
und done
), gibt Tupel zurück, die zwei verwandte, aber unterschiedliche Werte enthalten.
Die Klammern um Tupel können in bestimmten Situationen weggelassen werden, so dass mehrere Rückgabewerte einfacher zu implementieren sind. Zum Beispiel können wir eine Funktion erstellen, um sowohl positive als auch negative Quadratwurzeln einer reellen Zahl zurückzugeben:
julia> pmsqrt(x::Real) = sqrt(x), -sqrt(x)
pmsqrt (generic function with 1 method)
julia> pmsqrt(4)
(2.0,-2.0)
Mit Hilfe der Destrukturierungszuweisung können mehrere Rückgabewerte entpackt werden. Um die Quadratwurzeln in den Variablen a
und b
zu speichern, genügt es zu schreiben:
julia> a, b = pmsqrt(9.0)
(3.0,-3.0)
julia> a
3.0
julia> b
-3.0
Ein anderes Beispiel dafür sind die Funktionen divrem
und fldmod
, die gleichzeitig eine Ganzzahl- ( divrem
oder fldmod
) Division und eine divrem
fldmod
:
julia> q, r = divrem(10, 3)
(3,1)
julia> q
3
julia> r
1