简体   繁体   English

在 c++ 中展平一个多维向量

[英]Flatten a multidimensional vector in c++

I want to write a generic function in c++ to flatten any multidimensional vector provided.我想在 c++ 中编写一个通用的 function 来展平所提供的任何多维向量。 The signature of the method is as follows:该方法的签名如下:

template <class U, class T>
void flatten(U* out, T& inp, int* dims, int n){
  // out is the flattened output
  // inp is some multidimensional vector<vector...<U>>
  // dims is an array of the dimensions of inp
  // n is the number of dimensions of inp
  int ticker[n];
  int prodLimit = 1;
  int prod = 0;
  
  // calculate prodLimit as product of elements in dims and initialize ticker
  for (int i=0; i<n; i++){
    ticker[i] = 0;
    prodLimit *= dims[i];
  }
  
  while (prod < prodLimit){
    
    // access the current element in inp
    {...}

    // update ticker
    for (int i=n-1; i>0; i++){
      if (ticker[i] == dims[i]){
        ticker[i] == 0;
        ticker[i-1] += 1;
      }
    }
    prod += 1;
    out[prod] = correctElementIn_inp;
  }
}

Most of the operations are straight forward except accessing the specific component of multi-dimensional vector inp .除了访问多维向量inp的特定组件外,大多数操作都很简单。 As the dimensions are unknown a-priori I create an array of size n in a while loop to handle the counter for each dimension and update it properly.由于维度是先验未知的,我在 while 循环中创建了一个大小为n的数组来处理每个维度的计数器并正确更新它。 Now the only problem remaining is using the ticker to access the correct element in the vector.现在剩下的唯一问题是使用代码访问向量中的正确元素。

As an example lets say the following holds:例如,假设以下内容成立:

#include <vector>

typedef std::vector<std::vector<double>> vec2;
typedef std::vector<std::vector<std::vector<double>>> vec3;

int main(){
  vec2 inp1 = {...};
  vec3 inp2 = {...};
  
  int s1[2] = {2,3};
  int s2[3] = {2,3,4};
  ...

}

Now this method should be able to handle both of inp1 and inp2 .现在这个方法应该能够同时处理inp1inp2 Is there a way of recursively accessing the vector elements without explicitly using the vector element access operator for each case.有没有一种方法可以递归访问向量元素,而无需为每种情况显式使用向量元素访问运算符。

Your code is unecessarily complicated due to manually managing the memory and manually passing sizes around.由于手动管理 memory 并手动传递大小,您的代码不必要地复杂。 Both is obsolete when you use std::vector .当您使用std::vector时,两者都已过时。 Even if you do want a raw C-array as result you can nevertheless use a std::vector and later copy its contents to properly allocated C-array.即使您确实想要一个原始 C 数组作为结果,您仍然可以使用std::vector并稍后将其内容复制到正确分配的 C 数组。 I would use a recursive approach:我会使用递归方法:

#include <vector>
#include <iostream>

template <typename E,typename X>
void unroll(const std::vector<E>& v,std::vector<X>& out){
    std::cout << "unroll vector\n";
    out.insert(out.end(), v.begin(), v.end());
}


template <typename V,typename X>
void unroll(const std::vector<std::vector<V>>& v,std::vector<X>& out) {
    std::cout << "unroll vector of vectors\n";
    for (const auto& e : v) unroll(e,out);
}


int main() {
    std::vector<std::vector<std::vector<int>>> x;
    std::vector<int> y;
    x.push_back({{1,2,3},{4,5,6}});
    x.push_back({{7,8,9}});
    unroll(x,y);
    for (const auto& e : y) std::cout << e << " ";
}

Output : Output :

unroll vector of vectors
unroll vector of vectors
unroll vector
unroll vector
unroll vector of vectors
unroll vector
1 2 3 4 5 6 7 8 9 

Is there a way of recursively accessing the vector elements without explicitly using the vector element access operator for each case.有没有一种方法可以递归访问向量元素,而无需为每种情况显式使用向量元素访问运算符。

A vectors elements are stored in contiguous memory, hence you can use pointer arithmetics via its data() .向量元素存储在连续的 memory 中,因此您可以通过其data()使用指针算法。 However, a std::vector<std::vector<int>> does not store the int s in contiguous memory. Only the inner vectors elements are stored contiguously while each inner vector allocates the elements on the heap "somewhere".但是, std::vector<std::vector<int>>不会将int存储在连续的 memory 中。只有内部向量元素是连续存储的,而每个内部向量都会在堆上“某处”分配元素。 There is no short cut to access x[0][0][0] without accessing x[0][0] .没有访问x[0][0][0]而不访问x[0][0]的捷径。 Actually I would suggest to reconsider if you want to use nested vectors in the first place.实际上,如果您想首先使用嵌套向量,我建议您重新考虑。


PS: I was cheating a little;). PS:我有点作弊;)。 I would expect it to be more performant to first calculate the total size of out before pushing elements to it as you are doing in your code.我希望在将元素推送到它之前首先计算out的总大小会更好,就像您在代码中所做的那样。 For brevity it was left out from the above.为简洁起见,上面省略了它。 It can be done with similar recursion as the above code.可以使用与上述代码类似的递归来完成。 Instead of pusing to out you would accumulate some size until you reach the first non-vector element type.不是 push to out你会累积一些size直到你到达第一个非向量元素类型。 Then reserve enough space for out upfront and only then run the actual recursion that populates out .然后为out预留足够的空间,然后才运行填充out的实际递归。

If you have access to a C++20 compiler, I would use std::ranges::views::join recursively, to make a fully flattened view.如果您可以访问 C++20 编译器,我会递归地使用std::ranges::views::join来制作完全扁平化的视图。 You can then iterate this flat view without having to copy anything.然后您可以迭代此平面视图而无需复制任何内容。

using namespace std::ranges;

template<typename R>
concept nested_range = input_range<R> && range<range_reference_t<R>>;

struct flatten_t
{
    template<nested_range R>
    auto operator()(R && r) const
    {
        return std::forward<R>(r) | views::transform(*this) | views::join;
    }
    
    template<typename T>
    auto operator()(T && t) const
    {
        return std::forward<T>(t);
    }
};

template<typename T>
auto operator |(T && t, flatten_t f)
{
    return f(std::forward<T>(t));
}

constexpr flatten_t flatten;

int main() {
    std::vector<std::vector<std::vector<int>>> x;
    x.push_back({{1,2,3},{4,5,6}});
    x.push_back({{7,8,9}});
    for (const auto& e : x | flatten) std::cout << e << " ";
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM