NumPy C-APIにおけるイテレーション処理:`int PyArray_ITER_NOTDONE()` の詳細解説


機能

  • PyArray_Iter 構造体ポインタを受け取り、イテレーションの状態を返します。
PyArrayIter *iter;
int status;

iter = PyArray_IterNew(array);
if (iter == NULL) {
  // エラー処理
}

while ((status = PyArray_ITER_NOTDONE()) == PyArray_ITER_NOTDONE) {
  // イテレーション処理
  PyArray_ITER_NEXT(iter);
}

PyArray_IterFree(iter);

if (status < 0) {
  // エラー処理
}
  • エラーが発生した場合は、PyArray_IterGetErrno() 関数を使用してエラーコードを取得できます。
  • ループ処理内で PyArray_ITER_NOTDONE() を呼び出す必要はありません。PyArray_IterNext() 関数は自動的にイテレーションを進行させ、PyArray_ITER_FINISHED を返します。
  • PyArray_ITER_NOTDONE() はマクロであり、関数ではありません。


#include <numpy/arrayobject.h>

int main() {
  // 2次元配列を作成
  npy_intp dims[] = {2, 3};
  PyArray *array = PyArray_SimpleNew(2, dims, NPY_INT32);
  if (array == NULL) {
    return -1;
  }

  // 配列に値を設定
  int *ptr = (int *)PyArray_DATA(array);
  for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
      ptr[i * 3 + j] = i * 3 + j;
    }
  }

  // イテレータを作成
  PyArrayIter *iter = PyArray_IterNew(array);
  if (iter == NULL) {
    Py_DECREF(array);
    return -1;
  }

  // 配列をループ処理し、各要素の値を出力
  while (PyArray_ITER_NOTDONE() == PyArray_ITER_NOTDONE()) {
    int value = *((int *)PyArray_IterGetPtr(iter));
    printf("%d ", value);
    PyArray_IterNext(iter);
  }

  // イテレータを解放
  PyArray_IterFree(iter);

  // 配列を解放
  Py_DECREF(array);

  return 0;
}

コードの説明

  1. #include <numpy/arrayobject.h>: NumPy C-API ヘッダーファイルをインクルードします。
  2. // 2次元配列を作成: npy_intp dims[] = {2, 3}; は、2行3列の2次元配列の次元を定義します。PyArray_SimpleNew() 関数は、指定された次元とデータ型を持つ新しい配列を作成します。
  3. // 配列に値を設定: int *ptr = (int *)PyArray_DATA(array); は、配列のデータポインタを取得します。ループを使用して、各要素に値を設定します。
  4. // イテレータを作成: PyArray_IterNew(array) は、配列に対するイテレータを作成します。
  5. // 配列をループ処理し、各要素の値を出力: PyArray_ITER_NOTDONE() マクロを使用して、イテレーションが完了していないかどうかを確認します。PyArray_IterGetPtr() 関数は、現在のイテレーションの要素へのポインタを取得します。要素の値を printf() 関数を使用して出力します。PyArray_IterNext() 関数は、次のイテレーションに移動します。
  6. // イテレータを解放: PyArray_IterFree(iter) は、イテレータを解放します。
  7. // 配列を解放: Py_DECREF(array) は、配列の参照カウントを減らし、必要に応じて配列を解放します。

実行方法

このコードをコンパイルして実行するには、以下の手順が必要です。

  1. NumPyがインストールされていることを確認します。
  2. 以下のコマンドを使用してコードをコンパイルします。
gcc -o example example.c -lnumpy
  1. 以下のコマンドを使用してコードを実行します。
./example

このコードを実行すると、以下の出力が表示されます。

0 1 2 3 4 5
  • NumPy C-API には、他にも多くの関数やマクロが用意されています。詳細については、NumPy C-API リファレンスを参照してください。
  • このコードはあくまでも例であり、必要に応じて変更することができます。


PyArray_IterNext() 関数の戻り値を検査する

PyArrayIter *iter;

iter = PyArray_IterNew(array);
if (iter == NULL) {
  // エラー処理
}

while (PyArray_IterNext(iter) == 0) {
  // イテレーション処理
}

if (PyArray_IterNext(iter) < 0) {
  // エラー処理
}

PyArray_IterFree(iter);

PyArray_GetNDim(iter) と PyArray_IterGetIndex(iter) 関数を使用する

PyArray_GetNDim(iter) 関数は、イテレータの次元数を返します。PyArray_IterGetIndex(iter) 関数は、現在のイテレーションの各次元のインデックスを取得します。これらの関数を使用して、イテレーションが完了していないかどうかを確認できます。

PyArrayIter *iter;
int n_dim;

iter = PyArray_IterNew(array);
if (iter == NULL) {
  // エラー処理
}

n_dim = PyArray_GetNDim(iter);

while (1) {
  npy_intp indices[n_dim];

  for (int i = 0; i < n_dim; i++) {
    indices[i] = PyArray_IterGetIndex(iter, i);
  }

  // イテレーション処理

  if (PyArray_IterNext(iter) < 0) {
    break;
  }
}

PyArray_IterFree(iter);

for ループを使用する

NumPy 配列は Python のシーケンスとして扱えるため、for ループを使用してループ処理することもできます。

import numpy as np

array = np.arange(6).reshape(2, 3)

for value in array:
  print(value)
方法利点欠点
PyArray_ITER_NOTDONE() マクロを使用するシンプルでわかりやすいエラーが発生したときに詳細な情報が得られない
PyArray_IterNext() 関数の戻り値を検査するエラーが発生したときに詳細な情報が得られるコードが少し複雑になる
PyArray_GetNDim(iter)PyArray_IterGetIndex(iter) 関数を使用する柔軟性が高いコードが最も複雑になる
for ループを使用するPython コードで記述しやすいNumPy C-API を直接使用するよりも低速