假设我有一个多维点:
template <int dims> class Point { public: double data[dims]; };
现在我创建一个多维数组:
template <int dims> void foobar(int count0,...) { //Using variadic function. Could also use variadic templates in C++ (arguably better) int counts[dims],total_count=count0; counts[0]=count0; va_list args; va_start(args,count0); for (int i=1;i<dims;++i) { int count = va_arg(args,int); counts[i] = count; total_count *= count; } va_end(args); Point<dims>* array = new Point<dims>[total_count]; //... }
如您所见,array是一个未知维度的多维数组,以1D数组表示.
我的问题:我怎样才能将这个数组干净地初始化为多维网格点?
这是我想要的1,2和3维示例行为.显然,我不想为我可能想要使用的每个可能的维度编写这个!目标是概括这一点.
//Example: dim==1 for (int x=0; x<counts[0]; ++x) { Point<1>& point = array[x]; point.data[0] = (x+0.5) / (double)counts[0]; } //Example: dim==2 for (int y=0; y<counts[1]; ++y) { for (int x=0; x<counts[0]; ++x) { Point<2>& point = array[y*counts[0]+x]; point.data[0] = (x+0.5) / (double)counts[0]; point.data[1] = (y+0.5) / (double)counts[1]; } } //Example: dim==3 for (int z=0; z<counts[2]; ++z) { for (int y=0; y<counts[1]; ++y) { for (int x=0; x<counts[0]; ++x) { Point<3>& point = array[(z*counts[1]+y)*counts[0]+x]; point.data[0] = (x+0.5) / (double)counts[0]; point.data[1] = (y+0.5) / (double)counts[1]; point.data[2] = (z+0.5) / (double)counts[2]; } } }
同样,我的问题:以干净的方式概括上述任意数量的嵌套循环/维度.
注意:我提出了一些讨厌的方法,而且它们不够优雅和缓慢.特别是,如果可能的话,我想避免递归,因为这将经常在高维小数据集上调用.
注意:C中有明显的相似之处,因此C或C都可以. C 11是优选的.
解决方法
如果你需要表演和“优雅”,我会:
>删除多维数组方法并将其展平(即一个数组维度).没有新的,没有指针,使用std :: vector或std :: array的C现代方法.
>使用方便的方法为您的多Dim数组提供一个抽象容器,例如通用嵌套循环“generator”
>使用固定大小的数组替换可变参数(因为您在编译时知道dim.
所以我找到了一个与您的实现和需求非常一致的以下解决方案,并尝试保持简单.
我已经用“现代C 11方式”管理了一个小的MultiArray类重写.我在这里考虑在编译时可能不知道计数维度,因此现在使用std :: vector.当然可以使用std :: array获得更通用的编译时代码,请参阅下面的原始答案.
#include <iostream> #include <array> #include <vector> #include <numeric> template<size_t DIMS> class MultiArray { public: // Point here is just an array using Point = std::array<double,DIMS>; // fill data_ with an init array // not that count is just a fix sized array here no variadic arguments needed MultiArray(const std::array<size_t,DIMS>& count) : data_{init_array(count)} {} private: // the init functions are used for the constructor void init_point(Point& point,const std::array<size_t,DIMS>& coord,DIMS>& count) { std::cout << " -> { "; for (size_t i = 0; i < DIMS; i ++) { point[i] = (coord[i] + 0.5) / count[i]; std::cout << point[i] << ";"; } std::cout << " }\n"; } std::vector<Point> init_array(const std::array<size_t,DIMS>& count) { std::vector<Point> data(std::accumulate(count.begin(),count.end(),1,std::multiplies<int>())); // accumulate computes the prod of DIMS total_count std::array<size_t,DIMS> current{}; size_t i=0; do { for (size_t i = 0; i < DIMS; i ++) std::cout << current[i] << ";"; init_point(data[i++],current,count); } while (increment(current,count)); return data; } // the following function allows to imitate the nested loop by incrementing multidim coordinates bool increment( std::array<size_t,DIMS>& v,DIMS>& upper) { for (auto i = v.size(); i-- != 0; ) { ++v[i]; if (v[i] != upper[i]) { return true; } v[i] = 0; } return false; } private: std::vector<Point> data_; // A flatten multi dim vector of points }; int main() { std::array<size_t,3> count{{4,5,3}}; MultiArray<3> test{count}; }
正如您在结果中看到的那样,data_可以针对N维进行初始化.如果您需要更高级别的抽象类,可以在下面检查我的原始答案,您可以在其中执行一些方便的操作(即访问网格[{i,j,k}]以填充值).
原始答案
我需要一个满足我需求的多维网格,并且碰巧要求在code review上改进我的代码.这里有一个工作example,当然你可能不需要一些功能……我的实现与模板和编译时计算有关.请注意,尺寸大小必须在编译时知道.
简而言之,这个类看起来像这样:
template<typename T,size_t... DIMS> // variadic template here for the dimensions size class MultiGrid { // Access from regular idx such as grid[64] T& operator[] (size_type idx) { return values_[idx]; }; // Access from multi dimensional coordinates such as grid[{6,3,4}] T& operator[] (const std::array<size_t,sizeof...(DIMS)>& coord) { // can give code for runtime here }; private: std::array<T,sizeof...(DIMS)> data_; }
然后你可以构造你的多维数组并以这些方式初始化它:
MultiGrid<float,DIM1,DIM2,DIM3> data; // 3D // MultiGrid<float,DIM3,DIM4> data; // 4D // etc... // initialize it like this with nested arrays for (size_t z=0; z < DIM3; z ++) for (size_t y=0; y < DIM2; y ++) for (size_t x=0; x < DIM1; x ++) data[{x,y,z}] = [...] // whatever // or like this in C++11/14 way for (auto &x : data) x = [...] // this is convenient to provide a container like approach since no nested arrays are needed here.
如果您需要为可变参数嵌套循环指定算法以填充值,您可以查看here并使用第一个答案这样做:
// here lower_bound is 0-filled vector std::vector<int> current = lower_bound; do { data[current] = [...] // fill in where current is a coordinate } while (increment(current,lower_bound,upper_bound));
如果您需要我在实现中遗漏的内容,请随时提出.如果有人能指出改进,我也会很高兴.