Julia Language
tuples
Zoeken…
Syntaxis
- een,
- a, b
- a, b = xs
- ()
- (een,)
- (a, b)
- (a, b ...)
- Tuple {T, U, V}
- NTuple {N, T}
- Tuple {T, U, Vararg {V}}
Opmerkingen
Tuples hebben veel betere runtime-prestaties dan arrays om twee redenen: hun typen zijn preciezer en hun onveranderlijkheid stelt hen in staat om op de stapel te worden geplaatst in plaats van op de hoop. Echter, dit nauwkeuriger typen wordt geleverd met zowel meer compile-time overhead en meer moeite met het bereiken van het type stabiliteit .
Introductie tot Tuples
Tuple
zijn onveranderlijke geordende collecties van willekeurig verschillende objecten, van hetzelfde type of van verschillende types . Typisch worden tupels geconstrueerd met behulp van de (x, y)
syntaxis.
julia> tup = (1, 1.0, "Hello, World!")
(1,1.0,"Hello, World!")
De individuele objecten van een tuple kunnen worden opgehaald met behulp van indexeringssyntaxis:
julia> tup[1]
1
julia> tup[2]
1.0
julia> tup[3]
"Hello, World!"
Ze implementeren de iterabele interface en kunnen daarom worden herhaald met behulp van for
loops :
julia> for item in tup
println(item)
end
1
1.0
Hello, World!
Tuples ondersteunen ook een aantal generieke verzamelfuncties, zoals reverse
of length
:
julia> reverse(tup)
("Hello, World!",1.0,1)
julia> length(tup)
3
Verder tupels ondersteuning van een verscheidenheid van hogere orde collecties operaties, met inbegrip van any
, all
, map
, of 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
De lege tuple kan worden opgebouwd met ()
:
julia> ()
()
julia> isempty(ans)
true
Om echter een tuple van één element te construeren, is een volgkomma vereist. Dit komt omdat de haakjes ( (
en )
) anders zouden worden behandeld als groepeerbewerkingen in plaats van een tuple te construeren.
julia> (1)
1
julia> (1,)
(1,)
Voor de consistentie is een volgkomma ook toegestaan voor tupels met meer dan één element.
julia> (1, 2, 3,)
(1,2,3)
Tuple-typen
De typeof
een tupel is een subtype van Tuple
:
julia> typeof((1, 2, 3))
Tuple{Int64,Int64,Int64}
julia> typeof((1.0, :x, (1, 2)))
Tuple{Float64,Symbol,Tuple{Int64,Int64}}
In tegenstelling tot andere gegevenstypen zijn Tuple
typen covariant . Andere gegevenstypen in Julia zijn over het algemeen invariant. Dus,
julia> Tuple{Int, Int} <: Tuple{Number, Number}
true
julia> Vector{Int} <: Vector{Number}
false
Dit is het geval omdat overal een Tuple{Number, Number}
wordt geaccepteerd, net als een Tuple{Int, Int}
, omdat deze ook twee elementen heeft, die beide getallen zijn. Dat is niet het geval voor een Vector{Int}
versus een Vector{Number}
, omdat een functie die een Vector{Number}
accepteert misschien een drijvend punt (bijvoorbeeld 1.0
) of een complex nummer (bijvoorbeeld 1+3im
) wil 1+3im
in zo'n een vector.
De covariantie van tuple-typen betekent dat Tuple{Number}
(opnieuw in tegenstelling tot Vector{Number}
) eigenlijk een abstract type is:
julia> isleaftype(Tuple{Number})
false
julia> isleaftype(Vector{Number})
true
Concrete subtypen van Tuple{Number}
zijn Tuple{Int}
, Tuple{Float64}
, Tuple{Rational{BigInt}}
, enzovoort.
Tuple
typen kunnen een afsluitende Vararg
bevatten als hun laatste parameter om een onbeperkt aantal objecten aan te geven. Tuple{Vararg{Int}}
is bijvoorbeeld het type van alle tupels die een willekeurig aantal Int
s bevatten, mogelijk nul:
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
terwijl Tuple{String, Vararg{Int}}
tupels bestaande uit een string accepteert, gevolgd door een willekeurig aantal (mogelijk nul) Int
s.
julia> isa(("x", 1, 2), Tuple{String, Vararg{Int}})
true
julia> isa((1, 2), Tuple{String, Vararg{Int}})
false
Gecombineerd met co-variantie betekent dit dat Tuple{Vararg{Any}}
elke tuple beschrijft. Tuple{Vararg{Any}}
is inderdaad gewoon een andere manier om Tuple
zeggen:
julia> Tuple{Vararg{Any}} == Tuple
true
Vararg
accepteert een tweede parameter van het numerieke type die aangeeft hoe vaak de parameter van het eerste type precies moet voorkomen. (Standaard is deze parameter van het tweede type, indien niet gespecificeerd, een typevar die elke waarde kan hebben, daarom wordt een willekeurig aantal Int
's geaccepteerd in de Vararg
's hierboven.) Tuple
die eindigen op een opgegeven Vararg
worden automatisch uitgebreid naar de gevraagde aantal elementen:
julia> Tuple{String,Vararg{Int, 3}}
Tuple{String,Int64,Int64,Int64}
Er bestaat notatie voor homogene tupels met een opgegeven Vararg
: NTuple{N, T}
. In deze notatie geeft N
het aantal elementen in de tuple aan en geeft T
het geaccepteerde type aan. Bijvoorbeeld,
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)
Merk op dat NTuple
's boven een bepaalde grootte eenvoudig worden weergegeven als NTuple{N, T}
, in plaats van de uitgebreide Tuple
vorm, maar ze zijn nog steeds van hetzelfde type:
julia> Tuple{Int,Int,Int,Int,Int,Int,Int,Int,Int,Int}
NTuple{10,Int64}
Verzending op tuple types
Omdat de functieparameterlijsten van Julia zelf tuples zijn, is het verzenden van verschillende soorten tuples vaak eenvoudiger via de methodeparameters zelf, vaak met liberaal gebruik voor de "splatting" ...
-operator. Overweeg bijvoorbeeld de implementatie van reverse
voor tupels, van Base
:
revargs() = ()
revargs(x, r...) = (revargs(r...)..., x)
reverse(t::Tuple) = revargs(t...)
Implementatie van methoden op tupels op deze manier behoudt de stabiliteit van het type , wat cruciaal is voor de prestaties. We kunnen zien dat er geen overhead is aan deze benadering met behulp van de 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}
Hoewel enigszins moeilijk te lezen, wordt de code hier eenvoudigweg een nieuwe tuple gemaakt met respectievelijk de waarden 3e, 2e en 1e elementen van de oorspronkelijke tuple. Op veel machines komt dit neer op een uiterst efficiënte LLVM-code, die bestaat uit ladingen en opslag.
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
}
Meerdere retourwaarden
Tuples worden vaak gebruikt voor meerdere retourwaarden. Veel van de standaard bibliotheek, inclusief twee van de functies van de iterable-interface ( next
en done
) terug tupels met twee verwante, maar verschillende waarden.
De haakjes rond tupels kunnen in bepaalde situaties worden weggelaten, waardoor meerdere retourwaarden gemakkelijker kunnen worden geïmplementeerd. We kunnen bijvoorbeeld een functie maken om zowel positieve als negatieve vierkantswortels van een reëel getal terug te geven:
julia> pmsqrt(x::Real) = sqrt(x), -sqrt(x)
pmsqrt (generic function with 1 method)
julia> pmsqrt(4)
(2.0,-2.0)
Destructureringstoewijzing kan worden gebruikt om de meerdere retourwaarden uit te pakken. Om de vierkantswortels op te slaan in variabelen a
en b
, volstaat het om te schrijven:
julia> a, b = pmsqrt(9.0)
(3.0,-3.0)
julia> a
3.0
julia> b
-3.0
Een ander voorbeeld hiervan zijn de divrem
en fldmod
functies, die tegelijkertijd een integer (afkappend of gevloerd) deling en fldmod
uitvoeren:
julia> q, r = divrem(10, 3)
(3,1)
julia> q
3
julia> r
1