Szukaj…


Składnia

  • za,
  • a, b
  • a, b = xs
  • ()
  • (za,)
  • (a, b)
  • (a, b ...)
  • Tuple {T, U, V}
  • NTuple {N, T}
  • Tuple {T, U, Vararg {V}}

Uwagi

Krotki mają znacznie lepszą wydajność środowiska wykonawczego niż tablice z dwóch powodów: ich typy są bardziej precyzyjne, a ich niezmienność umożliwia przydzielanie ich na stosie zamiast na stosie. Jednak to bardziej precyzyjne pisanie wiąże się zarówno z większym nakładem czasu kompilacji, jak i trudnościami w osiągnięciu stabilności tekstu.

Wprowadzenie do krotek

Tuple są niezmiennie uporządkowanymi kolekcjami dowolnych odrębnych obiektów, tego samego lub różnych typów . Krotki są zwykle tworzone przy użyciu składni (x, y) .

julia> tup = (1, 1.0, "Hello, World!")
(1,1.0,"Hello, World!")

Poszczególne obiekty krotki można odzyskać za pomocą składni indeksowania:

julia> tup[1]
1

julia> tup[2]
1.0

julia> tup[3]
"Hello, World!"

Implementują iterowalny interfejs i dlatego można je powtarzać za pomocą pętli for :

julia> for item in tup
           println(item)
       end
1
1.0
Hello, World!

Krotki obsługują również różne ogólne funkcje kolekcji, takie jak reverse lub length :

julia> reverse(tup)
("Hello, World!",1.0,1)

julia> length(tup)
3

Ponadto krotki obsługują różne operacje związane z kolekcjami wyższego rzędu , w tym any , all , map lub 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

Pustą krotkę można zbudować za pomocą () :

julia> ()
()

julia> isempty(ans)
true

Jednak aby zbudować krotkę jednego elementu, wymagany jest przecinek końcowy. Wynika to z tego, że nawiasy ( ( i ) ) byłyby inaczej traktowane jako operacje grupujące razem zamiast tworzenia krotki.

julia> (1)
1

julia> (1,)
(1,)

W celu zachowania spójności dozwolony jest także przecinek końcowy dla krotek z więcej niż jednym elementem.

julia> (1, 2, 3,)
(1,2,3)

Typy krotek

typeof krotki jest podtypem Tuple :

julia> typeof((1, 2, 3))
Tuple{Int64,Int64,Int64}

julia> typeof((1.0, :x, (1, 2)))
Tuple{Float64,Symbol,Tuple{Int64,Int64}}

W przeciwieństwie do innych typów danych, typy Tuplekowariantne . Inne typy danych w Julii są zazwyczaj niezmienne. A zatem,

julia> Tuple{Int, Int} <: Tuple{Number, Number}
true

julia> Vector{Int} <: Vector{Number}
false

Dzieje się tak, ponieważ wszędzie Tuple{Number, Number} jest Tuple{Number, Number} , podobnie jak Tuple{Int, Int} , ponieważ ma ona również dwa elementy, z których oba są liczbami. Nie jest tak w przypadku Vector{Int} kontra Vector{Number} , ponieważ funkcja akceptująca Vector{Number} może chcieć przechowywać zmiennoprzecinkowy (np. 1.0 ) lub liczbę zespoloną (np. 1+3im ) wektor.

Kowariancja typów krotek oznacza, że Tuple{Number} (znowu w przeciwieństwie do Vector{Number} ) jest w rzeczywistości typem abstrakcyjnym:

julia> isleaftype(Tuple{Number})
false

julia> isleaftype(Vector{Number})
true

Konkretne podtypy Tuple{Number} obejmują Tuple{Int} , Tuple{Float64} , Tuple{Rational{BigInt}} i tak dalej.

Typy Tuple mogą zawierać kończący Vararg jako ostatni parametr wskazujący nieokreśloną liczbę obiektów. Na przykład Tuple{Vararg{Int}} jest rodzajem wszystkich krotek zawierających dowolną liczbę Int , prawdopodobnie 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

podczas gdy Tuple{String, Vararg{Int}} akceptuje krotki składające się z łańcucha , po którym następuje dowolna liczba (ewentualnie zero) Int .

julia> isa(("x", 1, 2), Tuple{String, Vararg{Int}})
true

julia> isa((1, 2), Tuple{String, Vararg{Int}})
false

W połączeniu z ko-wariancją oznacza to, że Tuple{Vararg{Any}} opisuje każdą krotkę. Rzeczywiście, Tuple{Vararg{Any}} to tylko inny sposób na powiedzenie Tuple :

julia> Tuple{Vararg{Any}} == Tuple
true

Vararg akceptuje drugi parametr typu numerycznego wskazujący, ile razy dokładnie powinien wystąpić jego pierwszy parametr typu. (Domyślnie, jeśli nie jest określony, ten drugi typ parametru jest typem maszynowym, który może przyjmować dowolną wartość, dlatego dowolna liczba Int jest akceptowana w Vararg powyżej.) Typy Tuple kończące się na określonym Vararg zostaną automatycznie rozwinięte do żądana liczba elementów:

julia> Tuple{String,Vararg{Int, 3}}
Tuple{String,Int64,Int64,Int64}

Istnieje notacja dla krotek jednorodnych z określonym Vararg : NTuple{N, T} . W tej notacji N oznacza liczbę elementów w krotce, a T oznacza zaakceptowany typ. Na przykład,

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)

Zauważ, że NTuple poza określonym rozmiarem są pokazane po prostu jako NTuple{N, T} , zamiast rozszerzonej formy Tuple , ale wciąż są tego samego typu:

julia> Tuple{Int,Int,Int,Int,Int,Int,Int,Int,Int,Int}
NTuple{10,Int64}

Wysyłka na krotki

Ponieważ listy parametrów funkcji Julii same są krotkami, wysyłanie różnych rodzajów krotek jest często łatwiejsze dzięki samym parametrom metody, często przy swobodnym użyciu operatora „splatting” ... Rozważmy na przykład implementację reverse dla krotek z Base :

revargs() = ()
revargs(x, r...) = (revargs(r...)..., x)

reverse(t::Tuple) = revargs(t...)

Implementowanie metod krotek w ten sposób zachowuje stabilność typu , co ma kluczowe znaczenie dla wydajności. Widzimy, że nie ma narzutu na to podejście przy użyciu makra @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}

Chociaż nieco trudny do odczytania, kod tutaj po prostu zaczyna tworzyć nową krotkę z wartościami odpowiednio 3, 2 i 1 elementu oryginalnej krotki. Na wielu komputerach kompiluje się to do niezwykle wydajnego kodu LLVM, który składa się z obciążeń i zapasów.

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
}

Wiele zwracanych wartości

Krotki są często używane do wielu zwracanych wartości. Znaczna część standardowej biblioteki, w tym dwie funkcje iterowalnego interfejsu ( next i done ), zwraca krotki zawierające dwie powiązane, ale odrębne wartości.

W niektórych sytuacjach nawiasy wokół krotek można pominąć, co ułatwia wdrożenie wielu wartości zwracanych. Na przykład możemy utworzyć funkcję zwracającą zarówno dodatnie, jak i ujemne pierwiastki kwadratowe liczby rzeczywistej:

julia> pmsqrt(x::Real) = sqrt(x), -sqrt(x)
pmsqrt (generic function with 1 method)

julia> pmsqrt(4)
(2.0,-2.0)

Przypisania destrukcyjnego można użyć do rozpakowania wielu zwracanych wartości. Aby zapisać pierwiastkowania w zmiennych i a b , wystarczy napisać:

julia> a, b = pmsqrt(9.0)
(3.0,-3.0)

julia> a
3.0

julia> b
-3.0

Innym przykładem tego są funkcje divrem i fldmod , które wykonują divrem na divrem całkowitych (odpowiednio obcinanie lub zmiennoprzecinkowe) i pozostałych operacji jednocześnie:

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
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow