C#やVB.NETに限らず、一般にプロブラミングの世界で配列を使う場合には、一次元配列よりも多次元配列配列を使う場合のほうが多いと思う。
例えば二次元の 6行 x 5列 の配列ならこんなイメージか。
図 二次元配列の例
さて、C#やVB.NETには、こんな多次元配列とは別に「多段階配列」と言うのがある。
英語で言うとジャグ配列(Jagged Array)と言うらしい。
Jagged とは英語でギザギザと言う形容詞だ。
例えば二次元のジャグ配列はこんな感じ。
図 二次元のジャグ配列(多段階配列)の例
この記事では、このジャグ配列(多段階配列)に付いて解説したい。
ワテの場合、こんな風に図解して視覚化しないと中々ジャグ配列を理解出来なかった。
なので、分かり易い図を描いてみた。
では本題に入ろう。
昔、 「ギザギザハートの子守唄」と言う歌が有ったらしい。
Jagged Heartと言うのか?それは知らない。
C#の普通の文字列型の二次元配列を復習する
まずは、基本に返ってC#の普通の文字列型の二次元配列を復習しよう。
class Program { static void Main(string[] args) { test0_普通の2次元配列(); } // 以下の二つの関数をこの中に入れておくと良い。 ・・・ }
C#のコンソールアプリケーションの場合、メイン関数が一つ必要になる。
そのProgramクラスの中に、以下のstatic関数を入れておく。
private static void test0_普通の2次元配列() { string[,] array = new string[6, 5] { {"00","01","02","03","04"}, {"10","11","12","13","14"}, {"20","21","22","23","24"}, {"30","31","32","33","34"}, {"40","41","42","43","44"}, {"50","51","52","53","54"} }; print_Array(array); }
この関数test0_普通の2次元配列()は見ての通り、6×5の大きさの二次元配列を定義すると同時に初期値を与えている。
なお、string[6, 5] の部分は string[,] のように要素数を省略しても良い。
次に、即席で作成したのが下に示す関数だ。
これで二次元文字列型配列の中身をズラズラと表示すると言う単純な関数だ。
private static void print_Array(string[,] array) { var i_low = array.GetLowerBound(0); var i_upp = array.GetUpperBound(0); for (var i = i_low; i <= i_upp; i++) { Console.Write("i={0} ", i); var j_low = array.GetLowerBound(1); var j_upp = array.GetUpperBound(1); for (var j = j_low; j <= j_upp; j++) { Console.Write("->{0}<-, ", array[i, j]); } Console.WriteLine(""); } }
このコードにおいて、二次元配列のインデックスの下限と上限を以下の手法で求めている。
これで一次元方向(i)の大きさが分る。
var i_low = array.GetLowerBound(0); var i_upp = array.GetUpperBound(0);
これで二次元方向(j)の大きさが分る。
var j_low = array.GetLowerBound(1); var j_upp = array.GetUpperBound(1);
なお、C#やVB.NETの場合には配列のインデックスは0から始まるので、この例ではGetLowerBound()の戻り値は0になる。
しかしながら、C#やVB.NETでもちょっとしたテクニックを使えば任意の数字を配列下限に設定する事も可能になる。
そんなヘンテコな手法を解説した記事がこちら。
さて、本題に戻って、早速Main関数を実行すると test0_普通の2次元配列()がコールされて実行出来た。
i=0 ->00<-, ->01<-, ->02<-, ->03<-, ->04<-, i=1 ->10<-, ->11<-, ->12<-, ->13<-, ->14<-, i=2 ->20<-, ->21<-, ->22<-, ->23<-, ->24<-, i=3 ->30<-, ->31<-, ->32<-, ->33<-, ->34<-, i=4 ->40<-, ->41<-, ->42<-, ->43<-, ->44<-, i=5 ->50<-, ->51<-, ->52<-, ->53<-, ->54<-, 続行するには何かキーを押してください . . .
まあ、これは文字列型の二次元配列の中身をズラズラと表示しているだけの単純な処理だ。
では、いよいよ肝心のジャグ配列を使ってみよう。
二次元のジャグ配列を使う
冒頭で紹介したジャグ配列の図を再び掲載した。
パワーポイントで作成したワテの力作だ。
図 二次元のジャグ配列(多段階配列)の例
この図に示すジャグ配列を作るC#コードがこれだ。
private static void test2_多段階配列を使う() { //x string[][] array = null; string[][] array = new string[6][]; array[0] = new string[] { "00", "01" }; array[1] = new string[] { "10", "11", "12", "13", "14" }; array[2] = new string[] { "20", "21", "22" }; array[3] = new string[] { "30", "31", "32", "33" }; array[4] = new string[] { "40", "41", "42", "43" }; array[5] = new string[] { "50" }; print_JaggedArray2(array); }
まあ、ワテの理解では、まず以下の宣言で1次元方向(i方向)に6部屋分を確保する。
string[][] array = new string[6][];
この6部屋は上図ではオレンジ色の部屋だ。
次に、そのオレンジの各部屋に二次元方向(j方向)の部屋を建て増しする。青色の部屋だ。
青色部屋の数は、iごとに自由に変えられる。
array[0] = new string[] { "00", "01" }; array[1] = new string[] { "10", "11", "12", "13", "14" }; array[2] = new string[] { "20", "21", "22" }; array[3] = new string[] { "30", "31", "32", "33" }; array[4] = new string[] { "40", "41", "42", "43" }; array[5] = new string[] { "50" };
このようにして、iとj方向にギザギザの間取りの平屋住宅を建てた感じだ。
なお、自分で値を代入して変数として利用出来るのは青色部屋だけだ。
オレンジの部屋は青色部屋を建てるために土台として使うので、他の用途には使えない。
つまりまあ C, C++の場合に例えて言えば、青色部屋の先頭アドレスの値をこのオレンジ部屋に入れておくと言う感じだ。
なのでオレンジ部屋はポインタ型の変数と言う感じか。
test2_多段階配列を使う()を早速実行してみる。
i=0 ->00<-, ->01<-, i=1 ->10<-, ->11<-, ->12<-, ->13<-, ->14<-, i=2 ->20<-, ->21<-, ->22<-, i=3 ->30<-, ->31<-, ->32<-, ->33<-, i=4 ->40<-, ->41<-, ->42<-, ->43<-, i=5 ->50<-, 続行するには何かキーを押してください . . .
なお、二次元の多段階配列配列の中身を表示する関数を即席で作成した。
二種類作ったが中身はほぼ同じ。
private static void print_JaggedArray(string[][] array) { for (var i = 0; i < array.Length; i++) { Console.Write("i={0} ", i); for (var j = 0; j < array[i].Length; j++) { Console.Write("->{0}<-, ", array[i][j]); } Console.WriteLine(""); } } private static void print_JaggedArray2(string[][] array) { int i = 0; foreach (var row_i in array) { Console.Write("i={0} ", i++); foreach (string column_j in row_i) { Console.Write("->{0}<-, ", column_j); } Console.WriteLine(""); } }
両者の関数の違いは、forループのやり方が i, j のインデックス方式か、インデックスを使わないforeach方式かの違いだ。
さて、これでジャグ配列をイメージする事が出来たと思う。
三次元のジャグ配列
ところが、ジャグ配列も二次元くらいまでならイメージし易いが、3次元とか4次元とかの多次元になると、イメージし辛い。
まあ、実際の場面では、3次元のジャグ配列なんて使う可能性は少ないとは思うが、あくまでここでは基本を身に付けると言う観点で3次元のジャグ配列を定義してみたい。
それがこれだ。
private static void test3_多段階配列を使う() { string[][][] array = new string[6][][]; array[0] = new string[2][]; array[1] = new string[5][]; array[2] = new string[3][]; array[3] = new string[4][]; array[4] = new string[4][]; array[5] = new string[1][]; array[0][0] = new string[] { "000", "001" }; array[0][1] = new string[] { "010", "011", "012" }; array[1][0] = new string[] { "100", "101", "102", "103", "104" }; array[1][1] = new string[] { "110", "111", "112", "113" }; array[1][2] = new string[] { "120", "121", "122", "123" }; array[1][3] = new string[] { "130", "131" }; array[1][4] = new string[] { "140", "141", "142" }; array[2][0] = new string[] { "200", "201" }; array[2][1] = new string[] { "210", "211", "212" }; array[2][2] = new string[] { "220", "221", "222" }; array[3][0] = new string[] { "300", "301", "302" }; array[3][1] = new string[] { "310", "311", "312", "313" }; array[3][2] = new string[] { "320", "321", "322", "323" }; array[3][3] = new string[] { "330", "331", "332" }; array[4][0] = new string[] { "400", "401" }; array[4][1] = new string[] { "410", "411", "412", "413" }; array[4][2] = new string[] { "420" }; array[4][3] = new string[] { "430", "431", "432", "433", "434" }; array[5][0] = new string[] { "500" }; // print_JaggedArray2(array); }
と言ってもサッパリ分からんと思う。
そこで再びパワーポイントの達人(自称)のワテが図解してみた。
図 三次元のジャグ配列のイメージ
まあ、例えて言えば、異端の建築家が設計したヘンテコなスタイルのマンションと言う感じ。
この場合は、青い部屋は黄色い部屋を建てる土台として使う。
従って、自分で好きなデータを入れられるのは黄色い部屋のみになる。
二次元なら配列要素には二つのインデックス i と j でアクセス出来る。
三次元になると、三番目のインデックス k とすれば、高さ方向に部屋が有る感じだ。
なお、図ではウッカリしていて k=0, 1, 2, … を書くのを忘れた。
さらにこの図のkインデックス方向(高さ方向)の黄色の部屋の数は、上のソースコード中で以下のように与えている初期値の要素数とは必ずしも一致していない。
array[0][0] = new string[] { "000", "001" }; // 要素数 2 array[0][1] = new string[] { "010", "011", "012" }; // 要素数 3 ・・・ array[5][0] = new string[] { "500" }; // 要素数 1
理由は、そこまで厳密に図を描くのが面倒だったので。
まあ、要するに手抜きと言うやつだ。
どうでしょうか?
これでC#やVB.NETの多段階配列(=ジャグ配列)をイメージする事が出来ましたでしょうか?
では、そんなあなたに練習問題を出そう。
ジャグ配列の練習問題
今まで見て来たジャグ配列は二次元でも三次元でもその要素は単純な一次元のデータ構造であった。
しかし、C#やVB.NETでは普通の配列でもジャグ配列でも、一次元データではなくて多次元のデータをミックスして配列を定義する事が可能だ。
例えばこんな感じ。
class 多段階配列Class { public static void test3() { int[] d1 = new int[2]; // 普通の一次元配列 int[][] d2 = new int[6][]; // 上でやった二次元ジャグ配列 d2[0] = new int[3]; d2[0][0] = 3; int[][,] d3 = new int[11][,]; // 二次元ジャグ配列だが、 d3[2] = new int[3, 4]; // 二次元目がさらに二次元配列 d3[2][2, 2] = 5; int[][][,] d4 = new int[11][][,]; // 三次元ジャグ配列だが、 d4[3] = new int[8][,]; // 三次元目がさらに二次元配列 d4[3][5] = new int[7, 9]; d4[3][5][6, 8] = 9; int[][,][][,] d5 = new int[11][,][][,]; // 四次元配列でもう訳分からん d5[3] = new int[8, 3][][,]; d5[3][5, 2] = new int[7][,]; d5[3][5, 2][6] = new int[2, 2] { { 0, 1 }, { 1, 2 } }; int[,,][,,][,] d6 = new int[4, 5, 6][,,][,]; // 三次元だがもう訳分からん d6[3, 4, 5] = new int[3, 2, 1][,]; d6[3, 4, 5][2, 1, 0] = new int[3, 3] { { 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 } }; } }
ここで何をやっているのかをイメージする事が出来れば、あなたにはジャグ配列初段の称号を与えよう(ワテの認定)。
まあ、ワテ認定初段ならMicrosoft認定プロフェッショナル(Microsoft Certified Professional)MCPに匹敵するくらいの実力があると言っても良いだろう。ほんまかいな。
まあ、こんなヘンテコな配列を使う場面は無いと思うが、例えばd5の四次元配列を使う場合には、
- まず new で一次元方向の配列を確保(一次元配列)
- 次にnew で二次元方向の配列を確保(二次元配列)
- 同様にnew で三次元方向の配列(一次元配列)を確保すると同時に四次元方向の配列(二次元配列)の初期値をセット
と言う流れになる。まあ初期値を与えなくても良いが。
それはどんな多次元配列、多段階配列を使う場合でも同じなので、じっくり自分で試してみると良い。
ちなみにジャグ配列は、C言語、C++言語において malloc()やcalloc()と言った動的にメモリを確保する関数で作る事が出来るメモリ構造に似ている。
ワテも昔、mallocを使って三次元のグリッド構造をメモリ内に確保してプロブラミングした経験がある。ある種の三次元シミュレーションプログラムだ。
ジャグ配列を使う場面はあるのか?
ワテの経験では、C#やVB.NETでプログラミングをする場合に、ジャグ配列を使う積極的な理由は無いと思う。
ジャグ配列のような長さが可変のデータ構造を使いたい場合には、配列ではなくてリスト List と言う機能がC#やVB.NETには導入されている。
配列を使うよりもリストのほうが遥かに便利だ。
何故なら、普通の配列でもジャグ配列でも、特定の位置の要素を削除するとか、特定の位置に要素を追加するとか、あるいは、二つの配列を連結するとか、そう言う処理はどれもややこしい。
一方、Listを使うとまさにそんな操作を自由自在に出来るのだ。
その辺りの解説はこの記事で詳しく説明している。
List<List<string>>を二次元文字列型ジャグ配列に変換
上の記事で説明しているListを使えば、例えば二次元文字列型ジャグ配列に相当するリストはこんな風に作成出来る。
public static string[][] ListListString() { var strLstLst = new List<List<string>>() { new List<string>(){ "00", "01" }, new List<string>(){ "10", "11", "12", "13", "14" }, new List<string>(){ "20", "21", "22" }, new List<string>(){ "30", "31", "32", "33" }, new List<string>(){ "40", "41", "42", "43" }, new List<string>(){ "50" } }; string[][] strArrArr = strLstLst.Select(lst_item => lst_item.ToArray()).ToArray(); return strArrArr; }
関数の末尾ではその List<List<string>> のデータ構造を、同じデータ構造を持つ二次元ジャグ配列に変換してみた。
元データが二重のList構造なので、toArray()メソッドを二回実行すれば二次元ジャグ配列化が出来る。
それをリターンして中身を表示してみる為には以下のように実行すれば良い。
var twoDim_jaggedArr = 多段階配列Class.ListListString(); print_JaggedArray(twoDim_jaggedArr);
その実行結果は、以下の通り。
i=0 ->00<-, ->01<-, i=1 ->10<-, ->11<-, ->12<-, ->13<-, ->14<-, i=2 ->20<-, ->21<-, ->22<-, i=3 ->30<-, ->31<-, ->32<-, ->33<-, i=4 ->40<-, ->41<-, ->42<-, ->43<-, i=5 ->50<-, 続行するには何かキーを押してください . . .
冒頭の二次元ジャグ配列の場合と同じになる。
逆変換も可能だ。
実際にやってみよう。
二次元文字列型ジャグ配列をList<List<string>>に変換
この場合もLINQを使うと簡単に出来る。
public static List<List<string>> TwoDimJaggedArr_to_LstLstStr() { string[][] array = new string[6][]; array[0] = new string[] { "00", "01" }; array[1] = new string[] { "10", "11", "12", "13", "14" }; array[2] = new string[] { "20", "21", "22" }; array[3] = new string[] { "30", "31", "32", "33" }; array[4] = new string[] { "40", "41", "42", "43" }; array[5] = new string[] { "50" }; List<List<string>> strLstLst = array.Select(arr_item => arr_item.ToList()).ToList(); return strLstLst; }
この場合には、二次元ジャグ配列に対して .toList() メソッドを二回実行する事により List<List<string>> 型のデータを得る事が出来る。
LINQを使うとこのようなデータ変換が簡単に出来るのだ。
LINQ否定派の人も未だにいるようだが、ワテも実は最初はLINQに馴染めなかったのだが、今ではLINQをバンバン使いまくる。
なぜなら、一旦使い始めるとその便利さに気付いて病みつきになる。
皆さんにもLINQをお勧めしたい。
まとめ
自称、ジャグ配列初段のワテが、C#やVB.NETにおけるジャグ配列を詳細な図解を用いて解説した。
これで皆さんもジャグ配列を完璧に理解出来るだろう。
しかしながら、配列を使うよりもリストを使うほうがとっても便利。
リストなら自由自在に要素を追加、削除も出来る。
複数のリストを連結して長い新リストを作る事も簡単。
配列でそんな操作をやろうとすると、出来なくはないがとても面倒。
と言う事でリストの利用をお勧めします。
なお、このページでジャグ配列の説明に利用したワテの力作の図はとっても分かり易い!
そんなあなたが、自称パワーポイントの達人のワテに何か作図して欲しい人はコメント欄とかメールで連絡頂ければ検討させて頂きます。無いか。
本で勉強する
まあ、楽しく勉強したい人はこんな本もお勧めだ。
超入門とは一体全体どんな入門なのか気になる。
ワテの場合、イデオムと言う単語の意味が分からん。でもアマゾンでは人気があるみたいだ。
この独習C#は有名だ。
アマゾンレビューの評価も高いのでお勧めだ。ただしワテは読んだことは無い。
やっぱり、こう言うポケットリファレンスを鞄に一冊だけしのばせておいて、時々気になった事が有れば辞書代わりに使う、そう言う使い方をしているとあなたも上級者っぽく見えるだろう。
- 単行本(ソフトカバー): 512ページ
- 発売日: 2017/6/20
- 梱包サイズ: 18.8 x 13 x 2.2 cm
なのでコンパクトだが512ページもあるのか!
かなり安値で本を買う(本以外も買えます)
2017年のゴールデンウイークを利用して便利なWEBサイトを作ってみた。
名付けて、
何が出来るかと言うと、
Amazon.co.jp
楽天市場
ヤフーショッピング
の三つのショッピングサイトを同時検索して、商品を価格の安い順に表示出来るお買い物支援サイトだ。
「最安価格サーチ」で、
「Androidプログラミング」をかなり安値で探したい人は こちらから >
「Xamarinプログラミング」をかなり安値で探したい人は こちらから >
「C#プログラミング」をかなり安値で探したい人は こちらから >
「Visual Studioプログラミング」をかなり安値で探したい人は こちらから >
もしお使い頂きまして何かご不明な点、改善案などありましたらお知らせ下さい。
コメント