タプル
タプル(英: tuple、英語発音: [tʌp(ə)l, tuːp(ə)l]、タプル、トゥープル)とは、複数の構成要素からなる組を総称する一般概念であり、カタカナ語としては主に計算機科学において順序付けられた対象の並びを表すために用いられる。n 個でできた組を英語で「n-tuple」と書くことに由来し、数学では日本語に訳す場合、通常「n 組」とし、タプルの概念そのものも組と呼ばれる。なお、 n-tuple は数学のタプルを意味するほか、同様に double、triple などの拡張として倍数詞の表現にも利用される(詳細は「倍#西洋数学における n 倍を表す表現」を参照)。
数学におけるタプル
[編集]集合論では n 組 (n-tuple) とは n 個の対象 a1, a2, ..., an の順序づけられた組であり、普通、括弧でくくって (a1, a2, a3, ..., an) のように書かれる。 タプルが順序づけられているということは、「2つの n 組 (a1, a2, ..., an) と (b1, b2, ..., bn) が等しい」という表現の意味することが、「対応する位置の要素がすべて等しいとき」すなわち「 (a1 = b1) ∧ (a2 = b2) ∧ ... ∧ (an = bn) であるとき、かつそのときに限る」ことを意味する。 タプルと直積集合には密接な関係があり、n 組の i 番目の対象が集合 Ai の要素とみなされるならば、n 組は直積集合 A1 × A2 × ... × An の要素である。
2 つの要素 a ∈ A, b ∈ B の順序づけられた組 (a, b) ∈ A × B は特に順序対 (ordered pair) と呼ばれる。 形式上は逆に、この順序対を元とすることによって一般の n 組 (n ≧ 2) を、例えば (a1, a2, a3, ..., an) = (...((a1, a2), a3), ..., an) のようにして構成的に定めることもできる。
タプルは様々な数学概念を定義するためにも利用される。 例えば、有限オートマトンは、入力アルファベットの有限集合 Σ, 状態の有限集合 S, 初期状態 s0 ∈ S, 遷移関数 δ : Σ × S → S, 受理状態の有限集合 F ⊆ S の 5 つ組 (quintuple, 5-tuple) (Σ, S, s0, δ, F) として定められ、有向グラフは、頂点集合 V と辺の集合 A ⊆ V × V の順序対 (V, A) として定められる。
なお数ベクトルも同じように要素の順序づけられた並びとして表されるが、数学の形式上、ある線形空間の要素とみなされるベクトルは実数、複素数など同じ体の要素からなるものである。 対して、タプルは任意の集合の要素を並べただけのずっと一般的な概念であるといえる。
語源
[編集]英語におけるタプルという用語は、シングル (single)、ダブル (double)、トリプル (triple)、さらに4つ組のクアドルプル (quadruple)、5つ組のクインチュープル (quintuple) といった、類似の接尾辞をもつラテン語系の英語の並びを抽象化して n タプル (n-tuple) と呼ぶようになったことに由来する。“-tuple”として表す用例は1863年から見られ[1]、集合論において独立した単語として扱われるようになり、その後Pythonのようなコンピュータ言語に取り入れられた。同様の数詞に関する英語の接尾辞から生まれた言葉としてアリティがある。
タプルの元となった要素の個数による個別の名称はフランス語に由来するラテン語系の語彙で、少なくとも「クインチュープル」までと「デキュプル」は輸入当時のフランス語から直接受け継いだものである。「 - チュープル」や「 - チュプル」となる個所を「 - タプル」と呼ぶこともある(クインタプル、セクスタプルなど)。また「クオドループル」は「カドラプル」や「クアドラプル」とも言う。
これらの概念が和訳される場合は、様々な名が当てられている。例としては「シングル」の場合は「一重」「一つ組」、「ダブル」ならば「二重」「二つ組」、「カドラプル」では「四重」「四つ組」、「デカプル」では「十重」「十個組」など。「シングル」「ダブル」「トリプル」については呼び方がさらにあり、シングルは「独」、ダブルは「ペア」やその和訳である「対」、トリプルは「鼎」が充てられることもある。
要素の数 | 個別の名称 | 英名 |
---|---|---|
1 | シングル | single |
2 | ダブル | double |
3 | トリプル | triple |
4 | クオドループル | quadruple |
5 | クインチュープル | quintuple |
6 | セクスチュープル | sextuple |
7 | セプチュプル | septuple |
8 | オクチュプル | octuple |
9 | ノニュプル | nonuple |
10 | デキュプル | decuple |
100 | センチュプル | centuple |
タプル型
[編集]型システム的には、代数的データ型でいう直積型そのものであり、C#、C++、Haskell、ML、Python、Scala、TypeScript といった多くのプログラミング言語にタプル型がある。いくつかは (x, y)
といったような構文でタプル型の値を直接記述できる。
動的な型付けを持つ言語では、コンテナ型を使うことで済まさせている場合もある。一方でPythonのように、長さを後から変えられないばかりでなく要素を変えることもできないというような、タプル専用のオブジェクトを用意している場合もある。
静的な型付けを持つ言語の場合、リスト型やコンテナ型の要素は、基本的になんらかの「同じ」型でなければならないので、それらの集積型をタプルの目的に流用することは不可能であり、そのためこの節の冒頭のようにタプルないし同等のものをサポートしていることが多い。一方で、簡易に定義できるデータクラスのようなクラスを用意して同等の目的に応えるKotlinのように、タプル型を提供していない言語もある。
Lispの場合
[編集](伝統的な)Lispの場合、基本的には線型リストを作るためのデータ構造であるコンスセル(cons cell)を、要素2個のタプル(二ツ組)に流用する。cons, car, cdr というリスト操作用の関数もそのまま流用される。格納するデータの型に制限はない。
(cons car部のデータ cdrのデータ)
car部のデータを取り出すには関数 car
を使い、cdr部のデータを取り出すには関数 cdr
を使う。
(setq x (cons 'A 1))
(setq a (car x)) ; a には 'A が入る
(setq b (cdr x)) ; b には 1 が入る
また、
(list データ1 データ2 データ3 … データN)
という関数呼び出しは、
(cons データ1 (cons データ2 (cons データ3 … (cons データN nil) … )))
という形、すなわち、car部にデータがあり、cdr部が後続のコンスセルになっており、最後のcdr部分を nil
という特殊な記号で終端させた二分木を返す。このような形式の二分木は「ちゃんとした」リストになっていると言えることから、「(プロパー)リスト」などという。
Python におけるタプル
[編集]Python におけるタプルは、任意の要素数の値をまとめてあたかもひとつの値のように扱う機能である。
タプルでは要素の型 (数値型、文字列型など)の同一性は不問であり、たとえば ('A', 1)
という文字列と整数をひと塊として変数に代入できる。
x = ('A', 1)
Pythonはゼロまたはひとつの要素となるタプルも認めており、とくに1要素のタプルはプログラミング言語としては珍しい。普通、そのようなものは計算の優先順位を変更するため、または1引数関数の引数を括るための括弧と区別できないからである。Pythonの場合、要素に続けてカンマを置くことで識別する。カンマを忘れるとタプルとして認識できなくなってしまうということでもある。
x = (1,) # 1要素の場合。()内末尾のカンマに注意
y = () # 0要素の場合。関数型言語ではユニットとして知られているが、タプルとは別に語られる
タプルは関数の返り値として使うこともできる。これによって、複数の値を返す関数を実現することができる。
def func():
return ('A', 1)
x = func() # x には ('A', 1) が入る
また、タプルは下記のようにして個別の要素を分離する。
x = ('A', 1)
(a, b) = x # a には 'A'、b には 1 が入る
Haskellにおけるタプル
[編集]ML系の関数型言語にほぼ共通して言えることとして、再帰的なデータ型であるリストとは異なり、タプルはそうでない点がある。また、リストの要素は単一の型のみを許容するのに対し、タプルの要素は型を問わない。これもデータ構造が再帰的か否かという点に由来するものである。また、要素へのアクセスはパターンマッチングか、先頭要素とその次の要素程度までを求める関数しかない。再帰的に定義されていないため、再帰関数で手繰ることも難しい(リストなら先頭と残りに分離することで容易に行える)。にもかかわらず、多くの関数型言語は60前後の要素まで許容されている。
Haskellでは、Pythonに類似した方法で利用できる部分もある。Pythonなどに影響を与えた側なので当然といえば当然である。
x = ('A', 1)
(a, b) = x -- a には 'A'、b には 1 が入る。パターンマッチの例
アクセス関数としては、第1要素を取得するfstと、第2要素を取得するsndがある。
x = ('A', 1)
a = fst x -- a には 'A' が入る
b = snd x -- b には 1 が入る
Haskellでは、2要素のタプルはデータ構築子 (,) で定義される。3要素なら (,,) で、最大の要素数は64であるため、これはカンマが63個並ぶことになる。以下はどちらも同じタプルが生成される。
x = ('A', 1)
y = (,) 'A' 1
もちろんセクション(演算子の部分適用)も可能である。
x = (, 'A')
f y = x -- y に1を与えたとすれば、fは (1, 'A') を返す
セクションなどでは、要素数に応じた構築子を使わなければならない。たとえば要素数が3ならば
f x y = (,,) x y 3
-- これは以下と等価である
f x y = (x, y, 3)
ユーティリティ関数としてcurryとuncurryが定義されている。いずれも2-tupleしか受け付けない。
curry id 'A' 1 -- id ('A', 1) と等価で、curryは ('A', 1) を返す
uncurry (+) (1,42) -- (+) 1 42 と等価で、これは 1 + 42 とも等価であるから、uncurryは43を返す
C# におけるタプル
[編集]C#言語におけるタプル(System.Tuple
クラス)は.NET Framework 4.0からサポートされた総称型のコンテナのひとつ。CLIで定義されたクラスであるためVisual Basic .NETでも同様に使用可能である。
var tuple1 = new Tuple<int>(1);
var tuple2 = new Tuple<string, int>("one", 1);
それぞれの値の取り出しは、Item1
, Item2
, ... にアクセスすることで行える。Pythonと違い、Tuple型で返したからといって複数の戻り値を返す関数とはならない。
var tuple2 = new Tuple<string, int>("one", 1);
string word = tuple2.Item1;
int number = tuple2.Item2;
他にも同様の総称型を持つ言語では、同様にしてタプルを使える[2]かもしれない。
なお、C#7.0からサポートされたタプル構文では、それ以前に存在していたSystem.Tuple
クラス(参照型)ではなく、新たに実装されたSystem.ValueTuple
構造体(値型)が使用されるようになっている。これにより使用時のインスタンス化が不要となった。
その他、各タプルフィールドへの命名(名前付きタプル)、分解構文(Deconstruct()
)を介した複数の戻り値の受け取りや、タプルそのものを直接ジェネリックの型引数として宣言することが可能となるなど、言語仕様の一部として統合された形で扱えるようになった。
// 名前なしタプル
var tuple1 = ("one", 1);
string word1 = tuple1.Item1;
int number1 = tuple1.Item2;
// 名前付きタプル
var tuple2 = (key: "one", value: 1);
string word2 = tuple2.key;
int number2 = tuple2.value;
// タプルの分解
(string str, int num) = tuple1;
// (string, string)タプルを型引数としたジェネリック型
Dictionary<string, (string, string)> dictionary;
TypeScript におけるタプル
[編集]TypeScript 1.3 から対応した。
var tuple: [string, number] = ["one", 1];
関係データベースにおけるタプル
[編集]関係データベースの理論である関係モデルでは、タプル(組)とはある関係(リレーション)を表(テーブル) として表したときの1つの行にあたり、形式的には、上述のタプルと同様に属性名を添字とした属性の型の直積集合の要素として表される。