Julia Language
タプル
サーチ…
構文
- a、
- a、b
- a、b = xs
- ()
- (a、b)
- (a、b)
- (a、b ...)
- タプル{T、U、V}
- NTuple {N、T}
- タプル{T、U、Vararg {V}}
備考
タプルは、2つの理由から、 配列よりもはるかに優れたランタイム・パフォーマンスを備えています。その型はより正確であり、その不変性により、ヒープではなくスタックに割り当てられます。しかし、このより正確な型定義には、コンパイル時のオーバーヘッドが増え、 型の安定性を達成するのが難しくなります。
タプルの紹介
Tuple
は、同じ型または異なる型の任意の異なるオブジェクトの不変の順序付けられたコレクションです。通常、タプルは(x, y)
構文を使用して作成されます。
julia> tup = (1, 1.0, "Hello, World!")
(1,1.0,"Hello, World!")
タプルの個々のオブジェクトは、インデックス構文を使用して取得できます。
julia> tup[1]
1
julia> tup[2]
1.0
julia> tup[3]
"Hello, World!"
iterableインタフェースを実装しているので、 for
ループを使っfor
反復することができます :
julia> for item in tup
println(item)
end
1
1.0
Hello, World!
タプルは、 reverse
またはlength
などのさまざまな汎用コレクション関数もサポートしています。
julia> reverse(tup)
("Hello, World!",1.0,1)
julia> length(tup)
3
さらに、タプルは、 any
、 all
、 map
、またはbroadcast
を含むさまざまな高次コレクション操作をサポートしmap
。
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
空のタプルは、 ()
を使って作ることができます:
julia> ()
()
julia> isempty(ans)
true
ただし、1つの要素のタプルを作成するには、末尾にカンマが必要です。これは、タプルを構築するのではなく、カッコ( (
および)
)をまとめてグループ化操作として扱うためです。
julia> (1)
1
julia> (1,)
(1,)
一貫性を保つために、複数の要素を持つタプルに対しても末尾のカンマを使用できます。
julia> (1, 2, 3,)
(1,2,3)
タプルタイプ
typeof
タプルのサブタイプであるTuple
:
julia> typeof((1, 2, 3))
Tuple{Int64,Int64,Int64}
julia> typeof((1.0, :x, (1, 2)))
Tuple{Float64,Symbol,Tuple{Int64,Int64}}
他のデータ型とは異なり、 Tuple
型は共変です。 Juliaの他のデータ型は一般的に不変です。このように、
julia> Tuple{Int, Int} <: Tuple{Number, Number}
true
julia> Vector{Int} <: Vector{Number}
false
これは、 Tuple{Number, Number}
どこでも受け入れられ、 Tuple{Int, Int}
も2つの要素を持ち、両方とも数値であるためです。つまりには当てはまらないVector{Int}
対Vector{Number}
受け付ける機能として、 Vector{Number}
浮動小数点を格納することを望むかもしれない(例えば、 1.0
)または複素数(例えば1+3im
ように)ベクトル。
タプル型の共分散は、 Tuple{Number}
(これはVector{Number}
とは異なりVector{Number}
)が実際に抽象型であることを意味します。
julia> isleaftype(Tuple{Number})
false
julia> isleaftype(Vector{Number})
true
Tuple{Number}
具体的なサブタイプには、 Tuple{Int}
、 Tuple{Float64}
、 Tuple{Rational{BigInt}}
などがあります。
Tuple
型は、無限の数のオブジェクトを示す最後のパラメータとして終了Vararg
を含むことがあります。たとえば、 Tuple{Vararg{Int}}
は、任意の数のInt
を含むすべてのタプルの型であり、おそらくゼロです。
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}}
は文字列とそれに続く任意の数(おそらくゼロ)のInt
からなるタプルを受け入れます。
julia> isa(("x", 1, 2), Tuple{String, Vararg{Int}})
true
julia> isa((1, 2), Tuple{String, Vararg{Int}})
false
共分散と組み合わせると、 Tuple{Vararg{Any}}
は任意のタプルを記述することを意味します。確かに、 Tuple{Vararg{Any}}
というのがもうひとつの方法であるTuple
:
julia> Tuple{Vararg{Any}} == Tuple
true
Vararg
は、第1の型パラメータが何回出現すべきかを示す第2の数値型パラメータを受け取ります。 (デフォルトでは、指定されていない場合、この第二のタイプのパラメータは、任意の数の理由で任意の値をとることができるtypevar、あるInt
sはに受け入れられVararg
sを超える。) Tuple
指定で終わるタイプVararg
自動的に展開されるであろうが要求された要素の数:
julia> Tuple{String,Vararg{Int, 3}}
Tuple{String,Int64,Int64,Int64}
指定されたVararg
: NTuple{N, T}
持つ均質タプルの表記法が存在する。この表記では、 N
はタプル内の要素の数を表し、 T
は受け入れられた型を表します。例えば、
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)
ことに留意されたいNTuple
の特定のサイズを超えては、単にとして示されているNTuple{N, T}
代わり拡大の、 Tuple
形態が、それらは依然として同じタイプです。
julia> Tuple{Int,Int,Int,Int,Int,Int,Int,Int,Int,Int}
NTuple{10,Int64}
タプル型のディスパッチ
Julia関数のパラメータリスト自体がタプルであるため 、さまざまな種類のタプルにディスパッチすることは、しばしばメソッドパラメータ自体を使用する方が簡単で、しばしば "splatting"演算子の自由な使用が容易...
。例えば、 Base
からのタプルのreverse
の実装を考えてみましょう。
revargs() = ()
revargs(x, r...) = (revargs(r...)..., x)
reverse(t::Tuple) = revargs(t...)
この方法でタプルにメソッドを実装すると、パフォーマンスにとって重要な型の安定性が維持されます。 @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}
読みにくいですが、ここでのコードは元のタプルの3番目、2番目、1番目の値を持つ新しいタプルを作成することです。多くのマシンでは、ロードとストアで構成される非常に効率的なLLVMコードにコンパイルされます。
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
}
複数の戻り値
タプルは、複数の戻り値に対して頻繁に使用されます。 イテラブルインタフェースの 2つの関数( next
とdone
)を含む標準ライブラリの多くは、2つの関連しているが異なる値を含むタプルを返します。
タプルの周りのカッコは、特定の状況では省略することができ、複数の戻り値を実装しやすくします。例えば、実数の正と負の平方根の両方を返す関数を作ることができます:
julia> pmsqrt(x::Real) = sqrt(x), -sqrt(x)
pmsqrt (generic function with 1 method)
julia> pmsqrt(4)
(2.0,-2.0)
destructuring代入を使用して、複数の戻り値をアンパックすることができます。変数a
とb
に平方根を格納するには、次のように書いてください。
julia> a, b = pmsqrt(9.0)
(3.0,-3.0)
julia> a
3.0
julia> b
-3.0
これの別の例は、 divrem
とfldmod
行う機能、 (それぞれ切り捨て又は床の)整数除算同時に及び剰余演算を:
julia> q, r = divrem(10, 3)
(3,1)
julia> q
3
julia> r
1