演算を行うための算術マジックベースのループを誰も提案していないことに、私はちょっとショックを受けました。 C. Wangはループがネストされていないソリューションを探しているので、次の方法を提案します。
double B[10][8][5];
int index = 0;
while (index < (10 * 8 * 5))
{
const int x = index % 10,
y = (index / 10) % 10,
z = index / 100;
do_something_on_B(B[x][y][z]);
++index;
}
さて、このアプローチはエレガントで柔軟ではないため、すべてのプロセスをテンプレート関数にまとめることができます。
template <typename F, typename T, int X, int Y, int Z>
void iterate_all(T (&xyz)[X][Y][Z], F func)
{
const int limit = X * Y * Z;
int index = 0;
while (index < limit)
{
const int x = index % X,
y = (index / X) % Y,
z = index / (X * Y);
func(xyz[x][y][z]);
++index;
}
}
このテンプレート関数は、ネストされたループの形式でも表現できます。
template <typename F, typename T, int X, int Y, int Z>
void iterate_all(T (&xyz)[X][Y][Z], F func)
{
for (auto &yz : xyz)
{
for (auto &z : yz)
{
for (auto &v : z)
{
func(v);
}
}
}
}
また、任意のサイズの3D配列と関数名を提供することで使用できます。これにより、パラメーターの推定により、各次元のサイズをカウントするハードワークが実行されます。
int main()
{
int A[10][8][5] = {{{0, 1}, {2, 3}}, {{4, 5}, {6, 7}}};
int B[7][99][8] = {{{0, 1}, {2, 3}}, {{4, 5}, {6, 7}}};
iterate_all(A, do_something_on_A);
iterate_all(B, do_something_on_B);
return 0;
}
より一般的な方へ
しかし、繰り返しになりますが、3D配列に対してのみ機能するため、柔軟性に欠けますが、SFINAEを使用して任意の次元の配列に対して作業を行うことができます。最初に、ランク 1の配列を反復するテンプレート関数が必要です。
template<typename F, typename A>
typename std::enable_if< std::rank<A>::value == 1 >::type
iterate_all(A &xyz, F func)
{
for (auto &v : xyz)
{
func(v);
}
}
そして、再帰を実行して、任意のランクの配列を反復する別のもの:
template<typename F, typename A>
typename std::enable_if< std::rank<A>::value != 1 >::type
iterate_all(A &xyz, F func)
{
for (auto &v : xyz)
{
iterate_all(v, func);
}
}
これにより、任意の次元の任意のサイズの配列のすべての次元のすべての要素を繰り返すことができます。
一緒に働く std::vector
複数のネストされたベクトルの場合、解は任意次元の任意サイズの配列の1つに似ていますが、SFINAEがありません。最初に、std::vector
s を反復して目的の関数を呼び出すテンプレート関数が必要です。
template <typename F, typename T, template<typename, typename> class V>
void iterate_all(V<T, std::allocator<T>> &xyz, F func)
{
for (auto &v : xyz)
{
func(v);
}
}
そして、あらゆる種類のベクターを反復して自分自身を呼び出す別のテンプレート関数:
template <typename F, typename T, template<typename, typename> class V>
void iterate_all(V<V<T, std::allocator<T>>, std::allocator<V<T, std::allocator<T>>>> &xyz, F func)
{
for (auto &v : xyz)
{
iterate_all(v, func);
}
}
入れ子のレベルに関係なく、は、iterate_all
値のベクトルのバージョンがより適切に一致しない限り、ベクトルのバージョンを呼び出し、再帰性を終了します。
int main()
{
using V0 = std::vector< std::vector< std::vector<int> > >;
using V1 = std::vector< std::vector< std::vector< std::vector< std::vector<int> > > > >;
V0 A0 = {{{0, 1}, {2, 3}}, {{4, 5}, {6, 7}}};
V1 A1 = {{{{{9, 8}, {7, 6}}, {{5, 4}, {3, 2}}}}};
iterate_all(A0, do_something_on_A);
iterate_all(A1, do_something_on_A);
return 0;
}
関数本体はかなり単純でわかりやすいと思います...コンパイラーがこのループを展開できるかどうか疑問に思います(ほとんどのコンパイラーが最初の例を展開できると私はほぼ確信しています)。
こちらからライブデモをご覧ください。
それが役に立てば幸い。