再入門C＃：配列

自分がいろいろ理解できていないことにきがついたので、自分的に復習してみることにした。自分が普段書いているからといって理解しているとは限らない。
はっきり言って内容はこの完璧ブログを読んだほうが良い。自分向けの文章です。

* [配列](https://ufcpp.net/study/csharp/st_array.html)

# 問題

プログラミングにおいて、複数の変数を計算したいような場合がある。数が少なければよいが、これがもし５０００とかあったらどうだろう？
配列はこのような問題を解くのに使う。

```c# 
        private static void NonArray()
        {
            int a = 0;
            int b = 1;
            int c = 5;
            int d = 9;
            int e = 3;
            Console.Write($"Sum: {a + b + c + d + e}");
        }
```

# 配列とは

配列は、データストラクチャの一つで、同じデータ型をもった固定長のコレクションを持ったものだ。別の変数を定義する代わりに、インデックスと呼ばれる数値を持っており、数値毎に固定の数をもっている。すべての配列は、連続したメモリ空間で構成されている。もっとも小さいアドレスが最初の要素を表し、最も大きいアドレスが最後の要素を表す。このあたりは、C#などの高級言語では隠蔽されていてイメージがつかみづらいが、ポインターのあるCなどを使うとイメージはわかりやすくなる。本記事ではこの説明は他の記事に譲ることにする。

# 配列を使用する

配列を使用するには、宣言をして、固定長のメモリ空間を確保する必要があります。宣言だけでは、メモリ空間を確保できないので、`new`キーワードを利用して、メモリを確保します。

```c#
配列型変数 = new 型名[配列の長さ]
```

具体的には

```C#
int[] a = new int[3];
```

これに対して値を格納することができます。

```C#
a[0] = 1; 
a[1] = 2;
a[2] = 3;
// a[3] はメモリが３つ分しか確保されていないのでできない
```

# 配列の初期化

配列に値を代入するのは面倒なので、時には初期化と一気にやってみたいでしょう。その時は次の構文が使えます。

```C#
型名[] 変数名 = new 型名[] { 値1, 値2, ...};
```

具体的には次のようになります。配列の長さに関しては要素が既にあるので、必要がありません。

```C#
int[] a= new int[] { 1, 2, 3 };
```

## 暗黙的型付け配列

C# 3.0 からは暗黙的型付け配列という書き方がサポートされていて、型名を省略することができます。型は、持っている要素から推論されます。

```C#
int[] a = new [] { 1, 2, 3 };
```

ややこしいやつだとどうなるのだろうと思い下記を試してみました。小数点がついているので、float か doubleかわからないと思ってこうしてみましたが、結果は double になりました。どこかに型推論のロジックがあるのかな？ちょっと調べたのですが見つからなかったので師匠に聞いてみます。**追記** コメントをいただきました。[浮動小数点数値型 (C# リファレンス)](https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/builtin-types/floating-point-numeric-types#real-literals)

```C#
var a = new[] { 0.1, 0.2, 0.3 };
```

# 範囲アクセス
C# 8.0 からは、`a[i..j]` という書き方で、i番目の要素からj番目の要素までを取り出すということができます。
サンプルをてみましょう。

```C#
        private static void RangeAccess()
        {
            var original = new[] { 1, 2, 3, 4 };
            var slice = original[1..3];
            foreach(var num in slice)
            {
                Console.WriteLine(num);
            }
        }
```

私は`2 3, 4` を想像したのですが、実際に出力されるのは、`2, 3` です。最後のインデックスは含まれないようです。他の書き方としては、最後の要素という書き方もできて`^` を使うことで、最後から数えて何番目の要素を表すことができます。詳しくは[インデックス・範囲処理](https://ufcpp.net/study/csharp/data/dataranges/)のブログを参照しましょう。


```C#
        private static void RangeAccess()
        {
            var original = new[] { 1, 2, 3, 4 };
            var slice = original[1..^1];
            foreach(var num in slice)
            {
                Console.WriteLine(num);
            }
        }
```

# 多次元配列を表現する

多次元配列は、２通りの書き方ができます。一つはこちら。古くからあるほう。配列の配列を使います。配列の中に配列を入れる考え方で、配列[][]の型で表します。例によって同時に初期化もできます。ちなみに `new int[5][6]` のような初期化の指定はできません。配列の配列にはいる配列は常に同じ長さでなくて良いからです。

```C#
             var multi = new int[5][]; // ok 
            var multiWithInitialize = new int[][]
            {
                new int[] {1,2},
                new int[] {1,2,3}
            };
```

こちらの場合のひとつの問題として、書くのが面倒なことがあります。上記の例も一つですが、例えば配列が格納されているときに、実際に含まれる配列が同じ長さなので、そのながさをとりたい場次のようになります。利点としては処理効率が次に紹介する多次元配列よりよりこと、要素の数が違う配列を格納できるの利点があります。

```C#
        private void Length(int[][] a)
        {
            var rowNum = a.Length;
            if (rowNum == 0) return;
            var colNum = a[0].Length;
        }
```

## 多次元配列

そこで、要素の数が同じ配列の場合、次の記法で、もっとシンプルに書くことができます。

```C#
型名[,] = new 型名[長さ１, 長さ2];
```

```C#
            var multi2 = new int[5, 5];
            var multi2WithIntialize = new int[,]
            {
                {1,2,3,4,5 },
                {6,7,8,9,10 }
            };
```

かなりすっきりしましたね。ちなみに、要素数を取得するのも、既にサイズがきまっているのですっきりと決まります。

```C#
        private void Length2(int[,] a)
        {
            var rowNum = a.GetLength(0);
            var colNum = a.GetLength(1);
        }
```


# まとめ

配列を復習してみました。わかっているつもりでしたが、型の暗黙の定義とか、配列の二つの指定方法の明確な使い分けなのは相当ぼんやりしかわかってませんでしたし、`a[i..j]` のレンジの記法も正確に理解してなかったことがわかりました。IDEや、Googleが補助してくれるので、普段は表面化しませんが、こういうことが、プログラマとしての足腰の強さにつながっている気がします。