ジュリアにおけるベクトルとタプルの違い
説明
ベクトルとタプルは一見似ているように見えますが、実際にはいくつかの重要な違いがあります。
この記事では、実際にコードを書いて使用する視点からの共通点と違いに焦点を当てます。Juliaでのタプルがどのような数学的根拠を持っているか、そしてそれがベクトル(リスト)とどう違うかについては、書籍Juliaプログラミングの $3.2.2$ タプルで詳しく説明しています。
共通点
複数の要素を含めることができ、インデックスとスライシングが可能です。
要素
ベクトルとタプルはどちらも複数の要素を持つことができます。
julia> v = [1, 2, 3]
3-element Vector{Int64}:
1
2
3
julia> t = (1, 2, 3)
(1, 2, 3)
インデックス
ベクトルとタプルはどちらもインデックスを通じて要素にアクセスできます。
julia> v[1]
1
julia> t[1]
1
スライシング
ベクトルとタプルはどちらもスライシングが可能です。
julia> v[1:2]
2-element Vector{Int64}:
1
2
julia> t[1:2]
(1, 2)
違い
不変性
タプルは不変immutableです。つまり、タプルの要素を変更することはできません。
julia> v[1] = 10
10
julia> v
3-element Vector{Int64}:
10
2
3
julia> t[1] = 10
ERROR: MethodError: no method matching setindex!(::Tuple{Int64, Int64, Int64}, ::Int64, ::Int64)
表記法
ベクトルは角括弧 []
を使用して表記し、タプルは丸括弧 ()
を使用して表記します。ベクトルはカンマを省略しても問題ありませんが、タプルはカンマを省略することはできません。実際、タプルは丸括弧を省略できます。また、ベクトルの場合、カンマを省略すると行ベクトルになります。
julia> [1 2 3]
1×3 Matrix{Int64}:
1 2 3
julia> (1 2 3)
ERROR: ParseError:
# Error @ REPL[16]:1:4
(1 2 3)
# └─┘ ── Expected `)`
julia> 1, 2, 3
(1, 2, 3)
形状
ベクトルは形状を変えることができますが、タプルは不可能です。
julia> u = [1, 2, 3, 4, 5, 6]
6-element Vector{Int64}:
1
2
3
4
5
6
julia> reshape(u, 1, 6)
1×6 Matrix{Int64}:
1 2 3 4 5 6
julia> reshape(u, 2, 3)
2×3 Matrix{Int64}:
1 3 5
2 4 6
julia> reshape(u, 3, 2)
3×2 Matrix{Int64}:
1 4
2 5
3 6
julia> reshape(t, 3, 1)
ERROR: MethodError: no method matching reshape(::Tuple{Int64, Int64, Int64}, ::Int64, ::Int64)
速度1
タプルは不変であるため、ベクトルよりも速いです。そのため、要素を変更する必要がなく、要素数が少ない場合はタプルを使用する方が有利です。しかし、大きなサイズの場合はベクトルの方がはるかに有利です。
julia> using BenchmarkTools
julia> @benchmark (1, 2, 3)
BenchmarkTools.Trial: 10000 samples with 1000 evaluations.
Range (min … max): 0.700 ns … 5.600 ns ┊ GC (min … max): 0.00% … 0.00%
Time (median): 0.700 ns ┊ GC (median): 0.00%
Time (mean ± σ): 0.741 ns ± 0.099 ns ┊ GC (mean ± σ): 0.00% ± 0.00%
█ ▂
█▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█ ▂
0.7 ns Histogram: frequency by time 0.8 ns <
Memory estimate: 0 bytes, allocs estimate: 0.
julia> @benchmark [1, 2, 3]
BenchmarkTools.Trial: 10000 samples with 999 evaluations.
Range (min … max): 13.514 ns … 1.907 μs ┊ GC (min … max): 0.00% … 98.16%
Time (median): 16.817 ns ┊ GC (median): 0.00%
Time (mean ± σ): 20.538 ns ± 64.293 ns ┊ GC (mean ± σ): 12.68% ± 4.04%
▄▄█▄▆▁▁
▂▂▃▃▆▇███████▆▆▄▄▃▃▃▃▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▃▃▃▃▂▂▂▂▂▂▁▂▂ ▃
13.5 ns Histogram: frequency by time 34.1 ns <
Memory estimate: 80 bytes, allocs estimate: 1.
タイプ昇格
ベクトルを定義する際にはタイプ昇格が基本的に適用されますが、タプルではそうではありません。1
と2.0
でベクトルとタプルを定義すると、ベクトルは要素のタイプがFloat64
に揃えられます。
julia> [1, 2.0]
2-element Vector{Float64}:
1.0
2.0
julia> Any[1, 2.0]
2-element Vector{Any}:
1
2.0
julia> (1, 2.0)
(1, 2.0)
Bogumił Kamiński, 데이터 분석을 위한 줄리아(Julia for Data Analysis, 류현지 역), p88 ↩︎