タプ

タプル の Julia

1個の演習

タプルについて

Tupleは、多くの点でVector(1次元のarray)によく似ています。ただし、いくつか重要な違いがあります。

  • タプルは角括弧[ ]ではなく丸括弧( )で書きます。ただし、意味が曖昧でない場合は丸括弧を省略できます。
  • タプルは同じ型でそろえる必要がありません。各要素の型は個別に保持されます。
  • タプルは不変です。

不変性によりコンパイラーがより多くの最適化を行えるため、タプルはベクトルよりも高いパフォーマンスを発揮します。そのため、可変である必要がない場合はタプルを選ぶとよいでしょう。

julia> t = (3, 5.2, "xyz")
(3, 5.2, "xyz")

# Non-homogeneous:
julia> dump(t)
Tuple{Int64, Float64, String}
  1: Int64 3
  2: Float64 5.2
  3: String "xyz"

julia> t[2]
5.2

# Immutable:
julia> t[2] = 7.3
ERROR: MethodError: no method matching setindex!(::Tuple{Int64, Float64, String}

Tuple()というコンストラクターもあり、これはイテラブルなコレクションをタプルに変換します。便利なことに、文字列を文字のイテラブルなコレクションとして扱ってくれます。

julia> Tuple("Julia")
('J', 'u', 'l', 'i', 'a')

どのタプルでも、要素にインデックスでアクセスしたり、要素を繰り返し処理したりできます。

数値だけからなるタプルは、配列とまったく同じように、sum()などの数学関数で使うこともできます。

展開

タプルは、多重代入を使うと個々の要素に展開できます。不要な要素には、ダミー変数としてアンダースコア_を使います。

julia> tup = (1, "Julia", true)
(1, "Julia", true)

julia> num, name, _ = tup
(1, "Julia", true)

julia> uppercase(name), 2num
("JULIA", 2)

タプルの全要素を別のタプル(より一般にはコレクション)にコピーするには、splat演算子...を使って展開します。詳しくは、関数のコンセプトで扱います。

julia> new_tup = (tup..., π, '😊')
(1, "Julia", true, π, '😊')

名前付きタプル

Tupleは値だけを持ちます。NamedTupleは、それぞれの値に名前を組み合わせたものです。

個々のフィールドには、「ドット」記法でアクセスできます。

julia> nt = (a = 1, b = 7.3)
(a = 1, b = 7.3)

julia> dump(nt)
@NamedTuple{a::Int64, b::Float64}
  a: Int64 1
  b: Float64 7.3

julia> nt.b
7.3

名前は内部的にSymbol(不変型で、名前の頭にはコロン:が付きます)として保存されるため、別の構文も使えます。

julia> nt[:b]
7.3

名前付きタプルの作成を2つのステップに分けると便利なことがあります。

  1. フィールドを独自のデータ型として定義します。
  2. これをコンストラクターとして使い、タプルを名前付きタプルに変換します。
# Step 1
julia> Student = NamedTuple{(:name, :grade), Tuple{String, Int}}
@NamedTuple{name::String, grade::Int64}

julia> typeof(Student)
DataType

# Step 2
julia> s = Student( ("Helen", 3) )
(name = "Helen", grade = 3)

julia> dump(s)
@NamedTuple{name::String, grade::Int64}
  name: String "Helen"
  grade: Int64 3

名前付きタプルを作る方法は驚くほどたくさんあるので、興味があればドキュメントを確認してみてください。

名前(「キー」とも呼ばれます)と値は、それぞれ別々に簡単に取り出せます。

julia> keys(s)
(:name, :grade)

julia> values(s)
("Helen", 3)

名前付きタプルを繰り返し処理すると、_値_だけが得られ、_名前_は無視されることに注意してください。

繰り返し処理で名前も使いたい場合は、もう少し手順が必要です。NamedTupleは、PairsのVectorに変換できます(Pairは別のコンセプトで扱うテーマです)。

julia> pairs(s)
pairs(::NamedTuple) with 2 entries:
  :name  => "Helen"
  :grade => 3

julia> collect(pairs(s))
2-element Vector{Pair{Symbol, Any}}:
  :name => "Helen"
  :grade => 3

このように、名前付きタプルは辞書によく似ています。ただし、Juliaの[Dict][dict]とはいくつか重要な点で異なります。

  • Dictは可変です。一方、NamedTupleは他のタプルと同じく不変です。
  • Dictは受け付けるキーの型が柔軟です。一方、NamedTupleはキーにシンボルしか使えません。

タプル(名前付きかどうかに関わらず)は、関数との間で構造化データを受け渡す非常によく使われる方法です。この用途では不変性が役立ち、データの安全性を保ちながら、コンパイラーによるより積極的な最適化を可能にします。

GitHubで編集 リンクは新しいウィンドウまたはタブで開きます

タプルを学習する