タプルとタプル型

2つの型 T1 と T2 が与えられた時、タプル型である (T1, T2) を作ることがきます。 これは @(T1, T2) のように書くこともできます。 exp1 と exp2 がそれぞれ T1 と T2 型の式であるとしましょう。 すると式 (exp1, exp2) または @(exp1, exp2) は (T1, T2) 型のタプルであることになります。 さらに、より多くの要素を持つタプルとタプル型を作ることもできます。 タプルにタプル型を割り振るために、 そのタプルとそのタプル型は持っている要素が同じ数でなければなりません。

タプルの式を評価する時、その要素すべてを 連続して (sequentially) 評価します。 その式が n 個の要素を持っていたと想像してみましょう。 するとその式の値は、 要素の順に列挙された n 個の要素の値によって構成されているタプルであることになります。

2 以上の長さ n のタプルは、フィールド名 0 から n-1 までの範囲を取る単なるレコードです。 タプル型 (T1, T2) の式 exp が与えられた時、(exp).0 や (exp).1 のような式を作ることができます。 これらはそれぞれ T1 型と T2 型を取ります。 式 exp は必ずしもタプルの式である必要はないことに注意してください。 例えば、exp は名前や関数適用であってもかまいません。 もし exp を評価して2つの値を持つタプルになった場合、 exp.0 は1番目の値に評価され、exp.1 は2番目の値に評価されることになります。 exp のタプル型がより多くの要素を持っていた場合、 当然このような式は一般化できます。

次に示す例では、はじめに長さが3のタプルを作り、 そのタプルの3つの要素全てに3つの名前を束縛しています。

val xyz = ('A', 1, 2.0) val x = xyz.0 and y = xyz.1 and z = xyz.2

作られたタプルには (char, int, double) というタプル型が割り振られることに注意してください。 タプルの要素を選択する他の方法には、次の例に示すパターンマッチがあります。

val xyz = ('A', 1, 2.0) val (x, y, z) = xyz // x = 'A'; y = 1; z = 2.0

(x, y, z) は、ぴったり3要素のタプルにマッチするパターンであることに注意してください。 その他のパターンマッチについては別の章で説明します。

ここまで紹介してきたタプルはしばしばフラットタプル、ネイティブタプル、アンボックス化タプルなどと呼ばれます。 ATS はまた別の種類のタプルをサポートしており、それはボックス化タプルと呼ばれています。 ボックス化タプルは本質的に、フラットタプルが配置されたヒープを指すポインタです。

また exp1 と exp2 がそれぞれ型 T1 と T2 の式であるとします。 式 '(exp1, exp2) はタプル型 '(T1, T2) のタプルになります。 結果的に、異なる要素数のボックス化タプルとボックス化タプル型も作ることができます。 注意するべきなのは、全てのボックス化タプルのサイズはポインタのものと同じであるということです。 そのためポインタが格納できる場所であれば、ボックス化タプルも格納することができます。 ボックス化タプルの使い方はアンボックス化タプルの使い方とよく似ています。 例えば、次のコードの意味はすぐにわかります。

val xyz = '( 'A', 1, 2.0 ) val x = xyz.0 and y = xyz.1 and z = xyz.2

'('A' の間に空白を入れないと、ATS/Postiats の今のパーサが混乱してしまうことに注意してください。

フラットタプルとボックス化タプルの可用性があると、 どちらの種類がより良いのか決める手掛かりを当然知りたくなるでしょう。 残念ながら私の知るかぎり、そのような方法はありません。 確実に言えることは、プロファイリングすることがしばしば必要になるということです。 けれども、もし GC を含まないコードを実行したいのであれば、 明らかにボックス化タプルを使うべきではありません。