1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

多次元配列のインデックスの順番

Last updated at Posted at 2019-10-20

はじめに

Juliaには多次元配列が用意されているが、CやJava育ちの人間からするとインデックスの付け方がとてもわかりにくいのでまとめてみた。

Javaの場合

Javaには多次元配列はなく、配列の配列が書けるだけだ。3次元の配列(というか配列の配列の配列)は
次のように書く。

int [][][] a = new int[2][3][4];

この場合、最内周になるのは一番右のインデックス(この場合は長さ4のもの)だ。

Cの場合

Cでは多次元配列も配列の配列も書ける。宣言の仕方は全然違うのだけど、使い方はおなじなので大変紛らわしい。

まず配列の配列。宣言はポインタのポインタのポインタとして定義して、mallocで中身を個別に確保する。面倒くさい。

  char ***array2;
  int counter = 0;
  array2 = (char ***) malloc(sizeof(char**) * 2);
  for (int i = 0; i < 2; i++) {
    array2[i] = (char **)malloc(sizeof(char*) * 3);
    for (int j = 0; j < 3; j++) {
      array2[i][j] = (char *)malloc(sizeof(char) * 4);
      for (int k = 0; k < 4; k++) {
        array2[i][j][k] = counter++;
      }
    }
  }

  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
      for (int k = 0; k < 4; k++) {
        printf("%d ", array2[i][j][k]);
      }
      printf("\n");
    }
    printf("\n");
  }

mallocの返り値チェックしていないとかは気にしないでほしい。

多次元配列の場合は、もう少し楽。メモリ領域が連続しているので一括で確保できる。注目してほしいのは出力部分でのアクセスの仕方が全く同じ形array[i][j][k]になっていること。

  char array[2][3][4];

  char * v =(char*) array;
  for (int i = 0; i < 24; i++)
    *v++ = i;

  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
      for (int k = 0; k < 4; k++) {
        printf("%d ", array[i][j][k]);
      }
      printf("\n");
    }
    printf("\n");
  }

出力はいずれの場合も

0 1 2 3 
4 5 6 7 
8 9 10 11 

12 13 14 15 
16 17 18 19 
20 21 22 23 

となる。いずれにしろ、最内周に対応するのは、1番右側のインデックスだ。

Python numpyの場合

numpy の場合もCやJavaと同じ。つまり最内周のインデックスは一番右。

>>> np.arange(0, 24).reshape(2,3,4)
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])

Juliaの場合

これがJuliaだと逆になる。つまり最内周に対応するのは、一番左のインデックスだ。全く同じような感じでreshapeしても結果がnumpyの場合と全く違うのがわかる。

julia> reshape(0:23, (2, 3, 4))
2×3×4 reshape(::UnitRange{Int64}, 2, 3, 4) with eltype Int64:
[:, :, 1] =
 0  2  4
 1  3  5

[:, :, 2] =
 6  8  10
 7  9  11

[:, :, 3] =
 12  14  16
 13  15  17

[:, :, 4] =
 18  20  22
 19  21  23

また、2次元配列を書く際の順番もカラム優先になっていることがわかる。

JuliaはPyCallでPythonのライブラリが呼び出せるのだけど、その時の値の受け渡しで、いろいろ面倒なことが起きそうな気がする。。

所感

多次元配列の場合には、どのインデックスを内周にするのかは言語設計者が任意に決められそうなのだけど、配列の配列を書く場合には、必然的に後ろ(右側)のインデックスが内周にならざるを得ない。

Juliaで最初のインデックスが内周になっているのはFortranの伝統なのだろうと思うのだけど、なんでFortranはそうしたんだろう。なにか必然性があるのか、ただ偶然そうしただけなのか。偶然だとするとかなり不幸な偶然だな。。

1
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?