줄리아에서 벡터와 튜플의 차이점
설명
벡터와 튜플은 얼핏보면 비슷해보이고, 실제로 공통점도 여럿 있기에 "같은 개념인데 이름만 다른건가?"하는 생각이 드는 건 자연스럽다. 하지만 벡터와 튜플은 몇 가지 중요한 차이점이 있다.
이 글에서는 주로 실제로 코드를 작성하고 사용하는 관점에서의 공통점과 차이점에 집중할 것이다. 줄리아에서 튜플이 어떤 수학적 근거를 가지고 있으며, 이것이 벡터(리스트)와 어떻게 다른지에 대해서는 책 줄리아 프로그래밍의 $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 ↩︎