ฉันรู้สึกตกใจมากที่ไม่มีใครเสนอลูปที่ใช้เวทย์มนตร์เลขคณิตให้ทำงาน เนื่องจาก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);
}
}
}
}
และสามารถใช้จัดเตรียมอาร์เรย์ 3 มิติที่มีขนาดโดยพลการบวกกับชื่อฟังก์ชันการปล่อยให้การหักพารามิเตอร์ทำงานอย่างหนักในการนับขนาดของแต่ละมิติ:
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;
}
ไปสู่ทั่วไปมากขึ้น
แต่อีกครั้งมันขาดความยืดหยุ่นเพราะมันใช้งานได้กับอาร์เรย์ 3 มิติเท่านั้น แต่เมื่อใช้ 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
สำหรับเวกเตอร์ที่ซ้อนกันหลายตัวโซลูชันจะมีลักษณะคล้ายกับอาร์เรย์ที่กำหนดขนาดตามอำเภอใจ แต่ไม่มี SFINAE ขั้นแรกเราจะต้องมีฟังก์ชันเทมเพลตที่วนซ้ำstd::vector
และเรียกฟังก์ชันที่ต้องการ:
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;
}
ฉันคิดว่าเนื้อหาของฟังก์ชันค่อนข้างเรียบง่ายและตรงไปตรงมา ... ฉันสงสัยว่าคอมไพเลอร์สามารถคลายการวนซ้ำนี้ได้หรือไม่ (ฉันเกือบแน่ใจว่าคอมไพเลอร์ส่วนใหญ่สามารถคลายตัวอย่างแรกได้)
ดูการสาธิตสดที่นี่
หวังว่าจะช่วยได้