Итерация по представлению boost :: multi_array

Я хочу понять, как использовать функциональность представления, предоставляемую boost :: multi_array. В частности, я хочу иметь возможность выполнять итерации в одном цикле по всем элементам представления, которое представляет конкретную подматрицу исходной матрицы (не обязательно непрерывную). Кажется, что предоставленный итератор не будет делать то, что я хочу (или что-нибудь, он не будет компилироваться).

В следующем примере у меня есть матрица 2×6, и я хочу получить ее подматрицу 2×4, поэтому, если я попытаюсь распечатать ее, я ожидаю получить «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;
//  }
}

Кто-нибудь знает, как перебрать только один цикл? Я пытался найти документацию, но не смог найти что-нибудь подходящее. Кроме того, если кто-нибудь знает другую библиотеку, которая может это сделать, дайте мне знать, спасибо!

0

Решение

Вот один из способов сделать это:

#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

Другие решения

Update2

Основываясь на ответе @rhashimoto, я попытался сделать некоторое обобщение. Со следующими функциями

// 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 предоставить метод под названием 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, ты можешь использовать auto it = b.data() вместо.

update1К сожалению, мое решение было неверным. Я только что нашел это, хотя b.origin() дает вам правильный итератор для начала, вы все еще перебираете multi_array вместо этого array_view.

0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector