Kimsenin işi yapmak için aritmetik-sihire dayalı bir döngü önermesi beni biraz şaşırttı. Yana C. Wang hiçbir iç içe döngüler ile çözüm aramaktadır , bir tane önerecek edeceğiz:
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;
}
Bu yaklaşım zarif ve esnek değildir, bu nedenle tüm süreci bir şablon işlevinde toplayabiliriz:
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;
}
}
Bu şablon işlevi, iç içe döngüler şeklinde de ifade edilebilir:
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);
}
}
}
}
Ayrıca, isteğe bağlı boyutun 3 boyutlu bir dizisi artı işlev adı sağlanarak kullanılabilir, bu da parametre çıkarımının her boyutun boyutunu saymanın zor işini yapmasına izin verir:
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;
}
Daha jeneriklere doğru
Ancak bir kez daha, esneklikten yoksundur, çünkü yalnızca 3B diziler için çalışır, ancak SFINAE'yi kullanarak işi keyfi boyuttaki diziler için yapabiliriz, ilk önce 1. sıra dizilerini yineleyen bir şablon işlevine ihtiyacımız var :
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);
}
}
Ve herhangi bir derecedeki dizileri yineleyen, özyinelemeyi yapan bir diğeri:
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);
}
}
Bu, rastgele boyutta rastgele boyutlu bir dizinin tüm boyutlarındaki tüm öğeleri yinelememizi sağlar.
İle çalışan std::vector
Birden çok iç içe vektör için çözüm, keyfi boyutlu keyfi boyutlu dizilerden birini yeniden andırır, ancak SFINAE olmadan: İlk olarak s'yi yineleyen std::vector
ve istenen işlevi çağıran bir şablon işlevine ihtiyacımız olacak :
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);
}
}
Ve her tür vektör vektörünü yineleyen ve kendisini çağıran başka bir şablon işlevi:
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);
}
}
İç içe geçme düzeyine bakılmaksızın, iterate_all
değerler vektörü sürümü daha iyi bir eşleşme olmadıkça, böylece özyinelemeyi sona erdirmedikçe, vektörlerin vektörü sürümünü çağıracaktır.
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;
}
İşlev gövdesinin oldukça basit ve anlaşılır olduğunu düşünüyorum ... Derleyicinin bu döngüleri açıp açamayacağını merak ediyorum (derleyicilerin çoğunun ilk örneği açabileceğinden neredeyse eminim).
Bkz burada canlı demo .
Umarım yardımcı olur.