たくのろじぃのメモ部屋

プログラミング(C#)の基礎やそれを応用した技術情報をメモしておくブログです。

【C#】WPFアプリケーション入門 #5 配列(多次元配列)

はじめに

前回は繰り返し処理をやりました。今回は配列について扱います。入れておきたいデータが複数ある場合、それぞれのデータを1つのグループにまとめることができます。

お品書き

  1. 配列
  2. 配列とfor 文
  3. 二次元配列
  4. 配列を使ったアプリ

1. 配列

配列は同じ型のデータを複数保管しておきたいときに使います。例えば、フルーツに関するデータを保管しておくとしましょう。

string fruit1 = りんご;
string fruit2 = バナナ;
string fruit3 = ぶどう;
...

と続けていくと確保するデータが肥大するだけでなく、すごく手間がかかります。そしてプログラムが長くなる!
こんなときは配列を使って、同じグループのものを 1 つの変数にまとめてしまえば良いのです。変数は箱という例えでしたが、配列は箱の仕切りだと思ってください。

string[] fruit = new string[] {"りんご", "バナナ", "ぶどう"};

このように宣言します。[] をつけると箱に仕切りができて、fruit の中には { } の中の要素が入っています。new は初期化の意味があり、予めデータを入れておくことができます。
{ }を消すと要素のデータは空っぽの状態で、配列が生成されます。ただし、要素をいくつ保管するかがわからないので、[ ]に要素数を書きます。
この中からデータを取り出すときには次のように書きます。

string[] fruit = new string[] {"りんご", "バナナ", "ぶどう"};
MessageBox.Show(fruit[0]);
MessageBox.Show(fruit[1]);
MessageBox.Show(fruit[2]);

fruitに [ ] をつけ、その中から何番目の要素を取り出すかを指定します。データは 0 番目から始まり、0 にはりんご、1 にはバナナ、2 にはぶどう が入っています。{ } の中に書いた順番に要素に保存されます。これが配列の考え方です。

2. 配列と for 文

上記の内容ではメッセージボックスを 3 回表示する処理になります。が、これだと同じ処理を何度も書くので面倒です。なので for 文を使ってスマートに表現してみます。

string[] fruit = new string[] {"りんご", "バナナ", "ぶどう"};
for (int i = 0; i < 3; i++)
{
    MessageBox.Show (fruit[i]);
}

このようにして要素を i とおけば、更新式で i が加算されるので 0 から順番に表示されます。これで実行してみるとメッセージが3回表示され、それぞれ「りんご」「バナナ」「ぶどう」と表示されます。
ただ、これには問題があります。それは要素数を超えた場合はどうなるかです。i < 3 ならば fruit[0] ~ fruit[2] までの要素で表示されますが、i < 4 にすると fruit[3] を呼び出そうとします。条件式を i < 4 にして実行してみると

f:id:takunology:20190704145201p:plain

エラーが表示されます。「インデックスが配列の境界外です」というのはそのままの意味です。要素が 3 つしかないのに 4 つ目を参照しても「ないんですけど」とエラーを吐かれます。

なので、配列を for 文でぶん回すときは気をつけて実装する必要があります。が、いちいち配列の要素数を数えてプログラムを実装するのも面倒な話です。そこで C# では便利な機能があります。配列の変数名.Length を条件式に組み込めば自動で要素数を取得してくれます。

string[] fruit = new string[] {"りんご", "バナナ", "ぶどう"};
for (int i = 0; i < fruit.Length; i++)
{
    MessageBox.Show (fruit[i]);
}

このようにすると、配列の要素の数だけ繰り返してくれます。という便利な機能の紹介でした。

3. 2次元配列 (多次元配列)

配列は箱に仕切りを入れるようなものと例えました。配列は1次元方向だけでなく、2次元方向にも拡張することが可能です。それどころか3次元、4次元・・・と拡張することができます。2次元以上に拡張した配列を多次元配列といいます。
2次元のイメージは面積だと思います。x 軸方向と y 軸方向をそれぞれ区切って、区画ごとにデータを保管するイメージです。実際にはコンピュータのメモリという部分を扱うのでデータ自体は1次元なのですが、複雑なので別枠で解説したいと思います。2次元配列はこのように宣言します。

int[,] table = new int[3, 3];

型の次に [,] をカンマで区切っています。カンマ1つで2次元配列になります。それを new で初期化しています。[3, 3] では各配列の要素数を指定します。今回は 3,3 なので合計 9 通りの要素を保管できます。
実際に2次元配列で確保した要素数を座標のように表してみます。

private void Button_Click(object sender, RoutedEventArgs e)
{
    int[,] table = new int[3, 3];
    for (int i = 0; i < table.GetLength(0); i++)
    {
        for (int j = 0; j < table.GetLength(1); j++)
        {
            MessageBox.Show(i + "," + j.ToString());
        }
    }
}

これで実行してみると、座標のように表示されます。

f:id:takunology:20190704151551p:plain

さて、今回は Length ではなく GetLength( ) をつかいました。何が違うかというと、後者は取得する要素数を次元で指定できる点が特徴です。今回は2次元配列なので、0と1の次元が生成されています。(コンピュータでは 0 から始まります。) 取得したい次元を指定すればその次元の要素数を取得できるのです。

for 文が二重になって複雑に見えるかもしれませんが、じっくり見ていけば何をしているかわかると思います。外側の for 文は座標で言うところの y 軸 (または行)、内側は x 軸 (または列) を示しています。面積で考えれば左上から右下にかけて順番に要素を読み取っています。

あとは int 型を文字列型にするために ToString() を用いています。

4. 配列を使ったアプリ

多次元配列と繰り返しなどを使って九九表を表示するアプリを作ってみたいと思います。
まずはデザインビューにて次のようなパーツを配置してください。

  • Label 2つ
  • Button 1つ
  • Textbox 3つ

このように配置します。

f:id:takunology:20190704153255p:plain

XAMLはこの通りです。

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="600">
    <Grid>
        <Button Content="Button" HorizontalAlignment="Left" Margin="97,150,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
        <Label Content="かける数" HorizontalAlignment="Left" Margin="81,59,0,0" VerticalAlignment="Top"/>
        <Label Content="かけられる数" HorizontalAlignment="Left" Margin="61,90,0,0" VerticalAlignment="Top"/>
        <TextBox HorizontalAlignment="Left" Height="23" Margin="136,62,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" x:Name="kakeru"/>
        <TextBox HorizontalAlignment="Left" Height="23" Margin="136,93,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" x:Name="kakerareru"/>
        <TextBox HorizontalAlignment="Left" Height="200" Margin="301,59,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="239" x:Name="result"/>
    </Grid>
</Window> 

続いてロジックを次のように編集します。

private void Button_Click(object sender, RoutedEventArgs e)
{
    // int.Parse はテキスト(文字列)を数値に変換
    // .Text はテキストボックスに入力された文字を取得
    int x = int.Parse(kakeru.Text);
    int y = int.Parse(kakerareru.Text);

    //テキストボックスで入力された数だけ要素を用意 [x,y]
    int[,] table = new int[x, y];

    //i と j を 1 から始めて積を配列に保管する
    for (int i = 1; i <= table.GetLength(0); i++)
    {
        for (int j = 1; j <= table.GetLength(1); j++)
        {
            //for文で配列要素を指定して積を保管する
            //配列は 0,0 から使用できるので注意!
            table[i - 1, j - 1] = i * j;
        }
    }

    result.Text = "計算結果\n";
    //配列の要素は 0 から始まるので注意!
    for (int i = 0; i < table.GetLength(0); i++)
    {
        for (int j = 0; j < table.GetLength(1); j++)
        {
            // 配列の要素に入っている結果を表示する
            result.Text += table[i, j].ToString();
            result.Text += " "; //空白を開ける
        }
        //一行終わったら改行
        result.Text += "\n";
    }
}

コメントアウトはちょっとした説明を加えています。このプログラムは2次元配列を使わなくてもできるのですが、2次元配列を使うために強引にやってみました。実行してみるとこんな感じになります。

f:id:takunology:20190704155504p:plain

Text += としているのは += に「追記する」という意味があり、=にしてしまうとテキストボックスの中身を初期化してしまいます。試しにやってみてください。最後に代入した文字しか表示されません。

おわりに

ちょっと強引に2次元配列を使ってプログラムを作成してみました。配列を使うと同じグループ属するデータを管理できるので変数をいくつも宣言する必要がなく、楽に実装できます。使い所はプログラムを書いていくにつれてわかると思います。とにかく慣れですね。

え、ポインタですか? C#にポインタ??