通过boost :: multi_array视图迭代

时间:2016-03-10 16:11:51

标签: c++ boost boost-multi-array

我想了解如何使用boost :: multi_array提供的视图功能。具体来说,我希望能够在视图的所有元素上的单个循环内迭代,该视图表示初始矩阵的特定子矩阵(不一定是连续的)。似乎提供的迭代器不会做我想要的(或任何东西,它不会编译)。

在下面的示例中,我有一个2x6矩阵,我希望得到它的2x4子矩阵,如果我尝试打印它,我希望得到" BoosLion"。事实上,如果我迭代每个维度就是这种情况。但是当我尝试使用单个迭代器进行迭代时,程序将无法编译。

#include <boost/multi_array.hpp>
#include <iostream>


int main()
{
  boost::multi_array<char, 2> a{boost::extents[2][6]};

  a[0][0] = 'B';
  a[0][1] = 'o';
  a[0][2] = 'o';
  a[0][3] = 's';
  a[0][4] = 't';
  a[0][5] = '\0';

  a[1][0] = 'L';
  a[1][1] = 'i';
  a[1][2] = 'o';
  a[1][3] = 'n';
  a[1][4] = 's';
  a[1][5] = '\0';
  typedef boost::multi_array<char, 2>::array_view<2>::type array_view;
  typedef boost::multi_array_types::index_range range;
  array_view b = a[boost::indices[range{0,2}][range{0,4}] ];

  for (unsigned int i = 0; i < 2; i++ ) {
    for (unsigned int j = 0; j < 4; j++ ) {
      std::cout << b[i][j] << std::endl;
    }
  }
//  I want to do something like this:
//  for (auto itr = b.begin(); itr < b.end(); ++itr) {
//    std::cout << *itr << std::endl;
//  }
}

有没有人知道如何只用一个循环进行迭代?我尝试搜索文档但无法找到任何相关内容。此外,如果有人知道另一个可以做到这一点的图书馆,请告诉我,谢谢!

2 个答案:

答案 0 :(得分:2)

以下是一种方法:

#include <iostream>
#include <boost/multi_array.hpp>

// Functor to iterate over a Boost MultiArray concept instance.
template<typename T, typename F, size_t Dimensions = T::dimensionality>
struct IterateHelper {
   void operator()(T& array, const F& f) const {
      for (auto element : array)
         IterateHelper<decltype(element), F>()(element, f);
   }
};

// Functor specialization for the final dimension.
template<typename T, typename F>
struct IterateHelper<T, F, 1> {
   void operator()(T& array, const F& f) const {
      for (auto& element : array)
         f(element);
   }
};

// Utility function to apply a function to each element of a Boost
// MultiArray concept instance (which includes views).
template<typename T, typename F>
static void iterate(T& array, const F& f) {
   IterateHelper<T, F>()(array, f);
}

int main() {
   boost::multi_array<char, 2> a{boost::extents[2][6]};

   a[0][0] = 'B';
   a[0][1] = 'o';
   a[0][2] = 'o';
   a[0][3] = 's';
   a[0][4] = 't';
   a[0][5] = '\0';

   a[1][0] = 'L';
   a[1][1] = 'i';
   a[1][2] = 'o';
   a[1][3] = 'n';
   a[1][4] = 's';
   a[1][5] = '\0';

   typedef boost::multi_array<char, 2>::array_view<2>::type array_view;
   typedef boost::multi_array_types::index_range range;
   array_view b = a[boost::indices[range{0,2}][range{0,4}] ];

   // Use the utility to apply a function to each element.
   iterate(b, [](char& c) {
      std::cout << c << std::endl;
   });

   return 0;
};

上面的代码定义了一个实用程序函数iterate(),您可以向其传递满足Boost MultiArray概念的对象(包括视图)和一个应用于每个元素的函数。实用程序功能通过使用Functor进行递归遍历每个维度。

CoLiRu

答案 1 :(得分:0)

UPDATE2

基于@rhashimoto提供的答案,我试图做一些概括。具有以下功能

} else {
    if(jj==-1) return false;

    i=++ii;
    j=jj+1;
}

您可以使用额外的参数将函数应用于每个元素。例如

// moving the judgement of dimensionality to the function's return-type
template<class T, class F>
typename std::enable_if<(T::dimensionality==1), void>::type IterateArrayView(T& array, F f) {
    for (auto& element : array) {
        f(element);
    }
}

template<class T, class F>
typename std::enable_if<(T::dimensionality>1), void>::type IterateArrayView(T& array, F f) {
    for (auto element : array) {
        IterateArrayView<decltype(element), F>(element, f);
    }
}

// given f() takes extra arguments
template<class T, class F, class... Args>
typename std::enable_if<(T::dimensionality==1), void>::type IterateArrayView(T& array, F f, Args& ...args) {
    for (auto& element : array) {
        f(element, args...);
    }
}

template<class T, class F, class... Args>
typename std::enable_if<(T::dimensionality>1), void>::type IterateArrayView(T& array, F f, Args& ...args) {
    for (auto element : array) {
        IterateArrayView<decltype(element), F, Args...>(element, f, args...);
    }
}

错误和旧解决方案

事实上,int main() { using array_type = boost::multi_array<int, 3>; using view_type = array_type::array_view<3>::type; using range = boost::multi_array_types::index_range; array_type data; data.resize(boost::extents[16][4][4]); view_type view = data[boost::indices[range(0,4)][range()][range()]]; int count = 0; IterateArrayView(view, [](int &i, int &count) { i = count++;}, count); std::cout << view[3][3][3] << std::endl; // output 63 (=4^3-1) return 0; } 提供了一种名为boost::multi_array_view的方法(参考:here):

origin()

所以你可以通过

循环它
template <typename T, std::size_t NumDims>
class multi_array_view :
  public const_multi_array_view<T,NumDims,T*>
{
    // a lot of code ...
    element* origin() { return this->base_+this->origin_offset_; }
    // a lot of code ...
};

对于array_view b; for (auto it = b.origin(); it != b.origin()+b.num_elements(); it++) { // do something, e.g. *it = 'a'; } ,您可以改为使用boost::multi_array

update1 :抱歉,我的解决方案不正确。我刚刚发现,虽然auto it = b.data()为您提供了正确的迭代器,但您仍然在循环遍历multi_array而不是此array_view。