特定用途向け C# 用 tuple クラス

まだ、実際に使ったわけではないけど。

わざわざ struct かくほどでもないなというときに、pair クラスは重宝されます。しかし、いちいち型を書くのがメンドクサイと、OCaml の tuple が欲しくなります。特に、特定用途で引数をすべて tuple にして、hash のキーとして突っ込むときなど、pair を入れ子にしていると死にそうになります。しかし、これと同等の物を作ろうとすると、型推論のない言語では結局 pair の入れ子と大差ないコストがかかって、やな感じになります。そこで型チェックはあきらめて、hash 値だけとれればいいや思ったとき、配列を使えばいいじゃんという結論に至ったわけですね。

class Tuple {
  private object array;

  public Tuple(object array) {
    this.array = array;
  }

  public static implicit operator Tuple(object[] os) {
    return new Tuple(os);
  }

  public override bool Equals(object o) {
    return this.Equals*1
        return false;
    return true;
  }

  public override int GetHashCode() {
    int h = 0;
    foreach (object o in array)
      h ^= o.GetHashCode();
    return h;
  }
}

わりといい加減ですが、この程度でも十分使えるでしょう。C# は int も object のサブクラスとして扱えるので、int や string の混ざった配列も渡せます。問題は、コンテナ系クラスの Equals メソッドが、値比較してくれないみたいで、ちょっとがっかり。implicit operator は object[] から Tuple へ暗黙の型変換です。別になくてもいいですが、少し手間が省けます。下のように動きます。

class Test {

  static void Main() {
    Tuple t = new object{1, 2, "a"};
    Tuple s = new object{1, 2, "a"};
    Tuple u = new object[]{1, 3, "a"};
    Console.WriteLine(t == s);
    Console.WriteLine(t.Equals(s));
    Console.WriteLine(t == u);
    Console.WriteLine(t.Equals(u));

    Dictionary dic = new Dictionary();
    dic[t] = 1;
    dic[u] = 2;
    dic[s] = 3;
    Console.WriteLine(dic[t]);
  }
}

実行結果

$ ./a
False
True
False
False
3

当然ですが、C++ ではこれはできません。Java もできますかね? できました。Object[] に整数リテラルを入れられるのは、Tiger からかな?

import java.util.HashMap;

class Tuple {
  Object array;

  public Tuple(Object array) {
    this.array = array;
  }

  public boolean equals(Object o) {
    Tuple t = (Tuple)o;
    for (int i = 0; i < array.length; i++)
      if (!array[i].equals(t.array[i]))
        return false;
    return true;
  }

  public int hashCode() {
    int h = 0;
    for (Object o: array)
      h ^= o.hashCode();
    return h;
  }

  public static void main(String args) {
    Tuple t = new Tuple(new Object{1, 2, "a"});
    Tuple s = new Tuple(new Object{1, 2, "a"});
    Tuple u = new Tuple(new Object{1, 3, "a"});

    System.out.println(t.equals(s));
    System.out.println(t.equals(u));

    HashMap hash = new HashMap();
    hash.put(t, 1);
    hash.put(u, 2);
    hash.put(s, 3);
    System.out.println(hash.get(t));
  }
}

*1:Tuple)o); } public bool Equals(Tuple o) { for (int i = 0; i < array.Length; i++) if (!array[i].Equals(o.array[i]