简体   繁体   English

通过boost :: multi_array视图进行迭代

[英]Iterate through a boost::multi_array view

I want to understand how to use the view functionality provided by boost::multi_array. 我想了解如何使用boost :: multi_array提供的视图功能。 Specifically, I want to be able to iterate within a single loop over all elements of a view that represents a particular submatrix of the initial matrix (not necessarily continuous). 具体来说,我希望能够在一个循环中对表示初始矩阵的特定子矩阵(不一定是连续的)的所有子视图进行迭代。 It seems that the iterator provided will not do what I want (or anything, it will not compile). 似乎提供的迭代器无法满足我的要求(或任何事情,它将无法编译)。

In the following example, I have a 2x6 matrix and I want to get its 2x4 submatrix so, if I try to print it I would expect to get "BoosLion". 在下面的示例中,我有一个2x6矩阵,我想获得其2x4子矩阵,因此,如果我尝试打印它,那么我期望得到“ BoosLion”。 Indeed this is the case if I iterate for each dimension. 确实,如果我对每个维度进行迭代,情况就是如此。 But when I try to do the iteration with a single iterator, the program will not compile. 但是,当我尝试使用单个迭代器进行迭代时,该程序将无法编译。

#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;
//  }
}

Does anyone know how to iterate with only a single loop? 有谁知道如何只用一个循环进行迭代? I tried searching the documentation but have been unable to find anything relevant. 我尝试搜索文档,但找不到任何相关内容。 Also, if anyone knows of another library that can do this, let me know, thank you! 另外,如果有人知道可以执行此操作的另一个库,请告诉我,谢谢!

Here is one way to do this: 这是执行此操作的一种方法:

#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;
};

The code above defines a utility function iterate() , to which you pass an object satisfying the Boost MultiArray concept (which includes views) and a function to apply to each element. 上面的代码定义了一个实用程序函数iterate() ,您将向其传递一个满足Boost MultiArray概念(包括视图)的对象以及一个应用于每个元素的函数。 The utility function works by using a Functor that iterates over each dimension recursively. 实用程序功能通过使用Functor进行工作,该Functor递归遍历每个维度。

CoLiRu CoLiRu

update2 更新2

Based on the answer provided by @rhashimoto, I tried to make some generalization. 根据@rhashimoto提供的答案,我尝试进行一些概括。 With the following functions 具有以下功能

// 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...);
    }
}

you can apply a function to each element with extra arguments. 您可以对带有附加参数的每个元素应用函数。 For example 例如

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;
}

wrong and old solution below 下面的错误和旧解决方案

In fact, boost::multi_array_view provide a method called origin() (ref: here ): 实际上, boost::multi_array_view提供了一种称为origin()的方法(参考: here ):

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 ...
};

So you can loop through it by 这样您就可以遍历

array_view b;
for (auto it = b.origin(); it != b.origin()+b.num_elements(); it++) {
    // do something, e.g.
    *it = 'a';
}

For boost::multi_array , you can use auto it = b.data() instead. 对于boost::multi_array ,可以使用auto it = b.data()代替。

update1 : Sorry, my solution was incorrect. update1 :对不起,我的解决方案不正确。 I just found that, although b.origin() gives you the correct iterator to begin with, you are still looping through the multi_array instead of this array_view. 我刚刚发现,尽管b.origin()为您提供了正确的迭代器,但您仍在遍历multi_array而不是该array_view。

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

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