Me sorprendió un poco que nadie propusiera algún bucle basado en aritmética-magia para hacer el trabajo. Dado que C. Wang está buscando una solución sin bucles anidados , propondré una:
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;
}
Bueno, este enfoque no es elegante y flexible, por lo que podríamos empaquetar todo el proceso en una función de plantilla:
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;
}
}
Esta función de plantilla también se puede expresar en forma de bucles anidados:
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);
}
}
}
}
Y se puede utilizar proporcionando una matriz 3D de tamaño arbitrario más el nombre de la función, dejando que la deducción del parámetro haga el trabajo duro de contar el tamaño de cada dimensión:
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;
}
Hacia más genérico
Pero una vez más, carece de flexibilidad porque solo funciona para matrices 3D, pero usando SFINAE podemos hacer el trabajo para matrices de una dimensión arbitraria, primero necesitamos una función de plantilla que itera matrices de rango 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);
}
}
Y otro que itera matrices de cualquier rango, haciendo la recursividad:
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);
}
}
Esto nos permite iterar todos los elementos en todas las dimensiones de una matriz de tamaño arbitrario de dimensiones arbitrarias.
Trabajando con std::vector
Para el vector anidado múltiple, la solución se asemeja a la de una matriz de tamaño arbitrario de dimensiones arbitrarias, pero sin SFINAE: Primero necesitaremos una función de plantilla que itera std::vectorsy llama a la función deseada:
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);
}
}
Y otra función de plantilla que itera cualquier tipo de vector de vectores y se llama a sí mismo:
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);
}
}
Independientemente del nivel de anidamiento, iterate_allllamará a la versión de vector de vectores a menos que la versión de vector de valores sea una mejor coincidencia, poniendo así fin a la recursividad.
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;
}
Creo que el cuerpo de la función es bastante simple y directo ... Me pregunto si el compilador podría desenrollar estos bucles (estoy casi seguro de que la mayoría de los compiladores podrían desenrollar el primer ejemplo).
Vea la demostración en vivo aquí .
Espero eso ayude.