C#のジェネリックで多次元マップを扱う方法

このQ&Aのポイント
  • C#でC++のテンプレートのような多次元マップを扱う方法について教えてください。
  • Dictionaryを使用して多次元マップを表現することは可能ですが、異なるキーごとに新たなDictionaryを作成する必要があります。
  • 他の方法としては、キーをペアにする方法もありますが、実装が煩雑になる可能性があります。スマートな方法や意見があれば教えてください。
回答を見る
  • ベストアンサー

C#のジェネリック

C++テンプレートの map<int , map< int , float > > a; a[1][1] = 10.0f; float val = a[1][1]; のような事をC#のジェネリックで実現することは可能でしょうか? Dictionary<int, Dictionary<int, float> > b = new Dictionary<int, Dictionary<int, float> >(); b[1][1] = 10.0f; とするとKeyNotExceptionがでます。 Dictionary<int, Dictionary<int, float> > b = new Dictionary<int, Dictionary<int, float> >(); b[1] = new Dictionary<int, float>(); b[1][1] = 10.0f; float val = b[1][1]; とすればできましたが第1キーが異なれば毎回newする必要があります。 キーをペアにする方法も試しました。 public struct Pair{ int x; int y; Pair(int _x, int _y) { x = _x; y = _y; } } Dictionary<Pair, float> c = new Dictionary<Pair, float>(); c[new Pair(1, 1)] = 10.0f; float val = c[new Pair(1, 1)]; しかしこれも無駄が多い気がします・・・ 自分なりにいろいろ試してみましたが他にスマートな方法、 あるいは意見があればお願い致します。

質問者が選んだベストアンサー

  • ベストアンサー
  • Xaval
  • ベストアンサー率58% (61/105)
回答No.2

ごめんなさい。急いでたので間違えました。 <T, X<U,V>>型は、キーが二つあるハッシュテーブルではありません。 >第1キーが異なれば毎回newする必要があります むしろこれで正解です。宣言はヒープにメモリを確保したに過ぎず、 nullしか入らないはずです。 Stackに確保するStruct型ならnewしなくても初期値が入るようになってますが。 構造上、a[][]はジャグあるいは方形配列で、また連想配列にするとすれば、2回の参照がありえます。{a->[]}->[] ですが、ジェネリック型は型をジェネリックにしてるに過ぎず、 参照先は1階層しかありません。 そっくりですが、オブジェクト指向てきにやってることが違うので、 まずはそれだけでの実装はできません。 そこで、ラッパークラスを作る必要があります。 class TowKeysDictionary<T,U>{ Dictionary<T, Dictionary<U,object>> dic1; // 適当に初期化しといて Dictionary<U,Object> innerDic; // 適当に初期化しといて public TowKeysCollection(){ ;} public object this[T key1, U key2]{ get{ // ここで実装 // key1 と key2で値を返してください。null注意。 } set{ // ここで実装 // key1とkey2で値を入れてください。 } } } これなら、 TowKeysDictionary<int, float> dic = new TowKeysDictionary<int,float>(); dic[1, 0.1] = aaaa; object b = dic[222, 333]; などが可能です。 なお、foreachで使いたいのなら、IDictionaryを継承してください。

furyfox
質問者

お礼

丁寧にご回答頂きありがとうございます。 なるほどラップして使うという方法、とても参考になりました。 IDirctionary< T , Dictionary<U,Object> >の継承を http://msdn2.microsoft.com/ja-jp/library/system.collections.idictionary(VS.80).aspx を参考にしてみましたがちょっとめんどくさいですね。 enumを使うだけならIEnumerableのみでもいいかなと思いました。 とりあえず参考までにXavalさんを元に簡単に作ってみました。 class TowKeysDictionary<T, U> : IEnumerable<KeyValuePair< T , Dictionary<U, Object>>> {   Dictionary<T, Dictionary<U, Object>> dictionary = new Dictionary<T, Dictionary<U, Object>>();   public TowKeysDictionary() { }   public Object this[T key1, U key2]   {     get     {       return dictionary[key1][key2];     }     set     {       if (!dictionary.ContainsKey(key1))       {         dictionary[key1] = new Dictionary<U, Object>();       }       dictionary[key1][key2] = value;     }   }   public IEnumerator<KeyValuePair<T, Dictionary<U, Object>>> GetEnumerator()   {     foreach (KeyValuePair<T, Dictionary<U, Object>> obj in dictionary)     {       yield return obj;     }   }   System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()   {     return ((IEnumerable<Dictionary<U, Object>>)this).GetEnumerator();   } }

その他の回答 (1)

  • Xaval
  • ベストアンサー率58% (61/105)
回答No.1

まずひとつ。 方形配列は使えませんか?flaot[,]a = new float[3,4]; また、キーが二つのディクショナリは、適当にクラスを作成してください。 public class AA<int,int>{ }

furyfox
質問者

お礼

ご回答ありがとうございます。 >方形配列は使えませんか?flaot[,]a = new float[3,4]; 分かりにくくてすみません。キーを2つのintにしているのは例です。 2次元配列として使いたい場合は上記のような例で良いと思いますが map< string , map< string , string > > といった場合も使う方法があるということでしょうか? あと[10000][10000]の部分しか使わない場合 flaot[,]a = new float[10001,10001];と宣言するのは 無駄にメモリを食いつぶしてしまいますよね? >また、キーが二つのディクショナリは、適当にクラスを作成してください。 >public class AA<int,int>{ >} 上記ですがちょっとよくわかりません。 当方の理解不足で申し訳ありません。 とりあえず自分なりに解釈すると public class AA<T1,T2>{ T1 x; T2 y; public AA(T1 _x, T2 _y) { x = _x; y = _y; } public override bool Equals(object obj) { AA<T1, T2> right = (AA<T1, T2>)obj; return (right.x.Equals(x) && right.y.Equals(y)); } public override int GetHashCode() { return x.GetHashCode() + y.GetHashCode(); } } でAA<int , int> を使うということでしょうか? だとすると質問の public struct Pair{ int x; int y; Pair(int _x, int _y) { x = _x; y = _y; } } と同じような気がします。 もし再度お答え頂ければ幸いです。

関連するQ&A

  • C言語の実数型の足し算

    C言語初心者です。関数の勉強していて、実数型計算に出くわしました。 #include <stdio.h> float add(float a, float b) { return a+b; } int main(void) { float x=10.5,y=20.3; printf("%f %f\n",x,y); printf("%f\n",add(x,y)); return 0; } としたら、 10.500000 20.299999 30.799999 という結果になりました。今のところint型でずーっと勉強していたので、20.3の20.299999表記が怪しく感じられ、結果も同様に怪しく感じられます。どうして、10.5+20.3=30.8とすっっきり表示してくれないのでしょうか。

  • C言語 プログラミングで行詰まりました…

    標準入力(キーボード)からi,jk,nの値を入力し、次の漸化式を計算し、X_0からX_nまで求めるプログラムを作成したいのですが、うまく表示されません。どかがおかしいのかご指摘お願いします。 <漸化式> X_n=(a+b)/X_(n-1) , X_0=c(n=0) ================================================================== #include<stdio.h> float f_X(int a,int b,float c) { float y; y=(a+b)/c; return y; } int main (void) { int number,i,j; float k,l,n,X; printf("i:"); scanf("%d", &i); printf("j:"); scanf("%d", &j); printf("k:"); scanf("%f", &k); printf("n:"); scanf("%f", &n); X=k; printf("X_0= %.6f\n",X); for(number=1;number<=n;number++) { l=f_X(i,j,X); printf("X_%d= %.6f \n",number,l); X=l; } return 0; } ===================================================================

  • C# マップエディタの描画方法

    現在マップエディターを作ろうとして 色々試しているのですが かなり迷走してきたので質問させてください 現在RPGツクールなどでよく見られる マップチップの配置をするところの描画で悩んでいるのですが private void Picture_Main_Paint(object sender, PaintEventArgs e) { float fSize = 1.0f; //float fSize = 0.5f; //float fSize = 0.25f; e.Graphics.ScaleTransform(fSize, fSize); for (int z = 0; z < 4; z++) {   for (int j = 0; j < 100; j++) // ループ回数は適当です   {     for (int i = 0; i < 100; i++)     {       int X = (int)(32 * fSize) * i;       int Y = (int)(32 * fSize) * j;       e.Graphics.DrawImage(bitmapBase[0, 0], new Point(X, Y));     }   } } } といったようにしているのですが 等倍ならまだしも縮小したときは画面分のループを行うので 処理が重すぎて使い物になりません。 マップエディタのような描画で なにかヒントになるようなものがあれば何でもいいので 教えていただけないでしょうか よろしくお願いします。

  • C言語の演算について

    次のプログラムを実行したらどう出力されますか。 微妙な代入演算の違いが分からないので、教えていただけないでしょうか。 #include<stdio.h> void main (void) { int x = 5; int y = 8; int z = 3; int a,b,c,d,e,f; a = y == x + z; b = !x; c = x + y / z; d = x *=z - 1; e = --y / --z; f = y+++ % x++; printf("%d,%d,%d,%d,%d,%d\n",a,b,c,d,e,f); } できれば途中のトレースも書いていただけると助かります。 よろしくお願いします。

  • プログラミングCの四則計算について質問です

    どうしてもわからなかったのでご指摘お願いします。 以下のプログラムを四則計算ができるプログラムに変更したいのですがどこを直せばいいのでしょうか? /* ansp5_7 */ #include <stdio.h> void wasa(int a,int b,int *w,int *x,int *y,float *z); main() { int d1,d2,wa,sa,seki; float syou; printf("data1,data2="); scanf("%d,%d",&d1,&d2); wasa(d1,d2,&wa,&sa,&seki,&syou); printf("wa=%d,sa=%d,seki=%d,syou=%5.2f\n",wa,sa,seki,syou); } void wasa(int a,int b,int *w,int *x,int *y,float *z) { *w=a+b; *x=a-b; *y=a*b; *z=(float)a/b; }

  • C言語のポインタの考え方について

    ポインタについて理解ができていないのでお聞きしたいのですが 値を交換する関数のプログラミングでこの場合ポインタ で以下にしないといけないと思います。 #include<stdio.h> void swap(int *a int *b){ int c; c=*a; *a=*b; *b=c; } main(){ int x,y; x=123; y=456; swap(&x,&y); printf("x = %d, y = %d\n", x, y); } またポインタを使用せず以下のプログラムではなぜダメのでしょうか。 よろしくお願い致します。 #include<stdio.h> void swap(int a int b){ int c; c=a; a=b; b=c; } main(){ int x,y; x=123; y=456; swap(x,y); printf("x = %d, y = %d\n", x, y); }

  • セグメンテーション違反

    穴掘り方というやり方で迷路を作っています。 とりあえずすこしだけ作ってみて実行したらセグメンテーション違反が起こってしまいました。 解説お願いします。 次のサイトを参考にしています。ttp://www.ced.is.utsunomiya-u.ac.jp/lecture/2009/prog/p3/kadai4/5.html #include<stdio.h> #include<stdlib.h> #include<time.h> #define A 51 #define B 51 /*51ラ51マスの迷路*/ void initialize(int *x,int *y,int map[A][B]);  /*最初のステージ作成と座標決定*/ int dig(int *x,int *y,int map[A][B]);   /*道を作る*/ int main(void) { int x,y;   /*現在の座標*/ int map[A][B];    initialize(&x,&y,map); dig(&x,&y,map); map[1][0] = 2; map[A-2][B-1] = 2; for(y=0;y<B;y++){ for(x=0;x<A;x++){ if( map[x][y] == 0){ printf(" "); }else if( map[x][y] == 1){ printf("■"); }else if( map[x][y] == 2){ printf("..") ; } } printf("\n"); } } void initialize(int *x,int *y,int map[A][B]) { int i,h; for(i=0;i<A;i++){ for(h=0;h<B;i++){ map[i][h]=1; } } do{ *x=rand()%A; *y=rand()%B; }while(*x!=0 && *x!=A-1 && *y!=0 && *y!=B-1); } int dig(int *x,int *y,int map[A][B]) { int r,c,dx,dy,count=0; do{ r = rand()%4; switch(r){     /*道を進める方向を決める*/ case 0: dx = 0; dy = -1; break; case 1: dx = -1; dy = 0; break; case 2: dx = 0; dy = 1; break; case 3: dx = 1; dy = 0; break;    } if(*x+dx*2 <= 0 || *y+dy*2 <= 0 || *x+dx*2 >= A-1 || *y+dy*2 >= B-1 || map[*x+dx*2][*y+dy*2] == 0){ c = 0; count++; if(count ==4){     /*4方向とも進めなかったらループを抜ける*/ break; } }else if(map[*x+dx*2][*y+dy*2] == 1){ map[*x+dx][*y+dy] = 0; *x = *x + dx*2; *y = *y + dy*2; c =1; } }while(c==0); }

  • C言語の演算について

    次のプログラムを実行したらどう出力されますか。 微妙な代入演算の違いが分からないので、教えていただけないでしょうか。 #include<stdio.h> void main (void) { int x = 5; int y = 8; int z = 3; int a,b,c,d,e,f; a = y == x + z; b = !x; c = x + y / z; d = x *=z - 1; e = --y / --z; f = y++ % x++; printf("%d,%d,%d,%d,%d,%d\n",a,b,c,d,e,f); } できれば途中のトレースも書いていただけると助かります。 よろしくお願いします。 なお、先ほど記述ミスがあるのにも関わらず投稿してしまいました…。 正しい記述はこちらの質問です。 大変失礼しました。 前の質問は削除可能になり次第、削除いたします。

  • Cで書かれたユークリッド互除法プログラムをJavaに

    a と b とを与えて d=GCD(a,b) と ax+by=d の解 x,y を与えるプログラム: #include<stdio.h> void f(int a,int b,int*d,int*x,int*y) { int x1,y1; if(b==0) {*x=(a>=0?1:-1); *y=0; *d=abs(a);} else { f(b,a%b,d,&x1,&y1); *x=y1; *y=x1-(a/b)*y1; } return;} int main(int argc, char**argv) { int a,b,d,x,y; if(argc!=3) exit(0); f(a=atoi(argv[1]), b=atoi(argv[2]), &d, &x, &y); printf("\n %d * %d + %d * %d = %d \n\n", a,x,b,y,d); } 上記のCプログラムをJavaに書き換えたいのですが、まったくといっていいほど手も足も出ません。 Javaはまだ初心者なもので… どなたか詳しい方、どうかアドバイス、またはご教授お願い致します。

    • ベストアンサー
    • Java
  • C#クラスについて教えてください

    下記通りクラスを作成した場合、プロパティaを配列にできますか? class Class1 { public string a { set; get; } public int b { set; get; } public int c { set; get; } } イメージとしては下記通りにしたいのですが、なかなかうまく行きません。 class Class1 { public Class1(int x) { } public string a[x] { set; get; } public int b { set; get; } public int c { set; get; } } private void Form1_Load(object sender, EventArgs e) { Class1 f = new Class1(2); f.a[0] = "asdf"; f.a[1] = "asdfa"; f.a[2] = "asdfasd"; f.b = 1; f.c = 2; } 初心者なんで、やさしく教えてください。お願いします。

専門家に質問してみよう