Tôi hơi sốc khi không ai đề xuất một số vòng lặp dựa trên phép thuật số học để thực hiện công việc này. Vì C. Wang đang tìm kiếm một giải pháp không có vòng lặp lồng nhau , tôi sẽ đề xuất một giải pháp :
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;
}
Chà, cách tiếp cận này không thanh lịch và linh hoạt, vì vậy chúng tôi có thể đóng gói tất cả quy trình vào một hàm mẫu:
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;
}
}
Hàm mẫu này cũng có thể được thể hiện dưới dạng các vòng lặp lồng nhau:
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);
}
}
}
}
Và có thể được sử dụng để cung cấp một mảng 3D có kích thước tùy ý cộng với tên hàm, cho phép việc khấu trừ tham số thực hiện công việc khó khăn khi đếm kích thước của mỗi chiều:
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;
}
Hướng tới chung chung hơn
Nhưng một lần nữa, nó thiếu tính linh hoạt vì nó chỉ hoạt động cho các mảng 3D, nhưng sử dụng SFINAE, chúng ta có thể thực hiện công việc cho các mảng có kích thước tùy ý, trước tiên chúng ta cần một hàm mẫu lặp lại các mảng có thứ hạng 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);
}
}
Và một cái khác lặp lại các mảng có thứ hạng bất kỳ, thực hiện đệ quy:
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);
}
}
Điều này cho phép chúng tôi lặp lại tất cả các phần tử trong tất cả các kích thước của một mảng có kích thước tùy ý có kích thước tùy ý.
Làm việc với std::vector
Đối với nhiều vectơ lồng nhau, giải pháp sẽ lắp lại một mảng có kích thước tùy ý có kích thước tùy ý, nhưng không có SFINAE: Đầu tiên, chúng ta sẽ cần một hàm mẫu lặp lại std::vector
s và gọi hàm mong muốn:
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);
}
}
Và một hàm mẫu khác lặp lại bất kỳ loại vectơ nào của vectơ và gọi chính nó:
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);
}
}
Bất kể mức độ lồng nhau, iterate_all
sẽ gọi phiên bản vectơ-of-vectơ trừ khi phiên bản vectơ-of-giá trị là một kết quả phù hợp hơn, do đó kết thúc đệ quy.
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;
}
Tôi nghĩ rằng phần thân hàm khá đơn giản và dễ hiểu ... Tôi tự hỏi liệu trình biên dịch có thể giải nén các vòng lặp này hay không (tôi gần như chắc chắn rằng hầu hết các trình biên dịch có thể giải nén ví dụ đầu tiên).
Xem demo trực tiếp tại đây .
Hy vọng nó giúp.