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


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow