どっちもいまいちTuple、どっちも便利Tuple

昼に投げといてもう一度Post、というのはちょっと分かったことがあったから。
そういえばC#ってジェネリックメソッドに型推論機能あったんですね。
知らんかったんですが、これでできることは多そうですね。
ということが実はSystem.Tuple.Create<>()だったりするわけですが。


C++は可変長テンプレート引数がきっかけとなってboost::tupleが標準ライブラリに編入。
.NET FrameworkではF#の導入に際してSystem.Tuple<>が導入されることになりました。
ということでこれは注目するべき事であるという話になるわけです。
そもそもタプルとは何かというと、型の違ういくつかのオブジェクトを投げ込む固定長のコンテナですね。
例えば、int・int・doubleの組を表すデータ構造が欲しかったとき。
Cではいちいち構造体を自作していた。

struct mystruct{
int x; int y; double z;
};
struct mystruct a={10,3,3.3}; //POD式初期化

Perl・Rubyではハッシュリストを使うのが普通かな。

a={:x=>10 , :y=>3 , :z=>3.3 } //Ruby

ハッシュ使うとかチートっぽいけどね。
まぁRubyだとこういうことは配列かハッシュでやるというのが相場ですかね。
しかしPythonにはタプルという仕組みがある。

a=(10,3,3.3)
x=a[0]

なお、Pythonのタプルは一度作ったら変更できないというのが特徴ですね。
なので安心してハッシュの添字にできるというのがウリ。


まぁ、Pythonのタプルは基本的には配列と一緒ですわ。
そういう意味で言うとPerl・Ruby・Pythonいずれも配列か何かでそれは実現できると。
しかしC++にせよC#にせよそういうわけにはいかないんですね。
なぜならば型というシステムがあるから。
RubyではFixnumもStringもArrayも同じ変数に入れられる。そして実行するときにそれが何があるか判定する。
けどC++もC#も、コンパイル時にこの変数はこの型のオブジェクトだと決めておくわけですね。
それでコンパイル時にかなりのことがわかるのでエラーが減って便利ですねと。
C# 4.0からはdynamicもあるけど、あれは例外だ。
というわけで型データの飛ばないことが重要だと。


タプルではないけれどC++にはstd::pairってのがありますね。あれは2個だけだが。
firstとsecondというフィールドがあります。まぁこの辺はC++でSTLといろいろやっていれば理解していると思います。
それを何個もにしたいということでboost::tupleとして導入されました。
その実装からして10変数までですね。まぁそれでもたいしたもんです。
そしてC++0xでは可変長引数テンプレートがあるので、無限にいけるはずだと。
可変長引数の使えるGCC4でコンパイル。

#include <iostream>
#include <tuple>
int main(void){
std::tuple<int,int,int,int,int,int,int,int,int,int,int,int> a(1,2,3,4,5,6,7,8,9,10,11,12);
//auto a=std::make_tuple(1,2,3,4,5,6,7,8,9,10,11,12);
std::get<2>(a)=100;
std::cout << std::get<2>(a) << std::endl;
}

なんで全部intやねんとか言わんといてね。実験のために適当にやっただけなんです。
なんかすごいめんどくさい書き方していますが、今のGCCでは無理だが、最終的には//で消したとおりのやり方で行ける。
さて、タプルから要素を取り出す方法がちょっと変な感じですね。
補助関数std::get()を使います。補助関数がいるというのが何とも言えないところです。
多分Nが変われば型が変わるからでしょうねぇ。
なお、C++では値は変更できます。それはまずいと思うのならconstつければOKです。
auto使ってたらどうするか? const autoにすればOKです。


C#のSystem.Tupleは可変長型引数とかないので、7個以下のオブジェクトを収めることしかできない設計です。
それでいいんかいという話だが、まぁその辺はちゃんと考えがあるわけです。
存在するSystem.Tupleのクラス(静的クラスは除く)は
System.Tuple<T1>
System.Tuple<T1,T2>
System.Tuple<T1,T2,T3>
System.Tuple<T1,T2,T3,T4>
System.Tuple<T1,T2,T3,T4,T5>
System.Tuple<T1,T2,T3,T4,T5,T6>
System.Tuple<T1,T2,T3,T4,T5,T6,T7>
System.Tuple<T1,T2,T3,T4,T5,T6,T7,TRest>
驚いたことが2つある。こいつら抽象クラスとか継承していないわけですよ。
だからこれらのTupleのクラスは兄弟クラスですらないわけです。
もう1つですが、8つ目のクラスはTRestというのがあります。これはTupleの型を書くらしい。
一体どうやって使うのかということ。


実際の使い方ですがこんな風にします。

var t1=System.Tuple.Create('a',10);
System.Console.WriteLine("{0}x{1}",t1.Item1,t1.Item2);
var t2=System.Tuple.Create(1,2,3,4,5,6,7,System.Tuple.Create(8,9,10,11,12));
System.Console.WriteLine(t2.Rest.Item2);

まず、基本の使い方ですが、これも補助メソッドで作って型推論で受けるのがおすすめです。
ジェネリックメソッドに型推論機能があることが分かったので安心して使えますね。
要素はItem1とかいう風なプロパティで取り出せます。簡潔でよろしい。
さて、8つ以上の要素を持つためにはどうするかというと、初め7つを投げ込んで、それ以降はそれでタプルにするという方法を取ります。
そのためのSystem.Tuple<T1,T2,T3,T4,T5,T6,T7,TRest>です。
とはいえ、これも作るのは補助メソッドで作って型推論で受けるのがおすすめ。
ここではintが12要素あるタプルを作っています。
しかし面白くないのはこの仕組みを使っても8つ目以上の要素を取り出すにはRestの何番目とやらないといけない。
だからここでは9番目を取り出しているのだが、わかりにくいですね。
というかこれなら8要素のTuple作って、使い方の提案としてこういう使い方を書けば良かったのに。
8要素Tuple作りたい人も7要素+Restに1要素Tuple入れて使わなきゃならんとかどうよ。
あと型名もずいぶん長くなってしまいます。型推論の導き出した型名は
System.Tuple<System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Int32,System.Tuple<System.Int32,System.Int32,System.Int32,System.Int32,System.Int32>>
とかいう長すぎる型名。var便利ですね。


ちなみに、C# 3.0ですでにタプルは存在したという意見もあります。それ匿名クラスね。
しかし匿名なので使い道が限られる。返り値にできないですからね。
あといちいちクラスを生成するのはどうよというのもある。
というわけでやっぱタプルちゃうわというのが普通の考えだと思います。
やっぱりタプルの使い道って返り値だと思いますから。そのための選択肢ができたのはよろしい。
.NETのは2~7要素の品質はかなりいいですから便利に使えると思います。
C++のそれも決して悪くはありません。ちょっと混乱するところではあるけどね。
ああ、もちろん返り値の型名は型推論は使えないのでちゃんと書いてね。
けど受ける方は型推論でOKなので活用しちゃって下さい。