Необычное индексирование в C ++ с использованием boost :: range - PullRequest
9 голосов
/ 26 августа 2011

Я бы хотел использовать boost :: range для получения чего-то похожего на «необычную индексацию», доступную в NumPy и Matlab. В частности, я хотел бы выбрать определенные элементы одного индексируемого контейнера, используя элементы другого контейнера в качестве индексов. Например, в Python можно сделать следующее:

>>> squares = numpy.arange(10)**2  # Step 1 - setup squares
>>> indices = numpy.array([1,3,4]) # Step 2 - setup indices
>>> squares[indices]               # Step 3 - fancy indexing
array([ 1, 9, 16])

В C ++, используя boost :: range, я думаю, что приведенный выше код будет выглядеть примерно так:

#include "sampled.hpp"
#include <boost/assign/std/vector.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/counting_range.hpp>
#include <iostream>
#include <vector>

using namespace boost;
using namespace boost::adaptors;
using namespace boost::assign;
using namespace boost::lambda;
using namespace std;

int main(int argc, char** argv)
{
    // Step 1 - setup squares
    vector<int> squares;
    push_back(
        squares,
        counting_range(1,11) | transformed(ret<int>(_1 * _1))
    );

    // Step 2 - setup indices
    vector<size_t> indices;
    indices += 1,3,4;

    // Step 3 - fancy indexing
    for_each(
        squares | sampled(indices),
        cout << _1 << constant(" ")
    );

    return 0;
}

Поскольку имя «indexed» уже используется существующим адаптером диапазона (boost :: adapter :: indexed), я назвал еще не реализованный адаптер индексации «дискретизированным» в приведенном выше коде.

Кто-нибудь знает, существует ли такой адаптер где-нибудь в boost или есть реализация, которой он хотел бы поделиться? Я начал пытаться реализовать это сам, сначала написав «sampled_iterator» (используя iterator_adaptor), а затем «sampled_range» (используя iterator_range), но я нахожу это довольно сложным.

1 Ответ

2 голосов
/ 27 августа 2011

ОК, поэтому мне удалось заставить что-то работать, используя boost :: permutation_iterator в качестве основы для адаптера диапазона.Поскольку используется permutation_iterator, я назвал адаптер диапазона «переставленным», а не «сэмплированным», как указано в выдержке из кода вопроса.Таким образом, версия кода на языке C ++ выглядит следующим образом:

// Step 3
for_each(
    squares | permuted(indices),
    cout << _1 << constant(" ")
);

Вот код для "permuted":

#pragma once

#include <boost/range/adaptor/argument_fwd.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/iterator/permutation_iterator.hpp>

template <class IndexContainer, class ElementRange>
class permuted_range :
    public boost::iterator_range<
        boost::permutation_iterator<
            typename boost::range_iterator<ElementRange>::type,
            typename IndexContainer::iterator> >
{
private:
    typedef boost::iterator_range<
        boost::permutation_iterator<
        typename boost::range_iterator<ElementRange>::type,
        typename IndexContainer::iterator> > base;

public:
    permuted_range(IndexContainer& i, ElementRange& r) :
        base(
            boost::make_permutation_iterator(boost::begin(r), i.begin()),
            boost::make_permutation_iterator(boost::begin(r), i.end()))
    { }
};

template <class IndexContainer>
struct permuted_holder : boost::range_detail::holder<IndexContainer>
{
    permuted_holder(IndexContainer i) :
        boost::range_detail::holder<IndexContainer>(i)
    { }
};

template <class ElementRange, class IndexContainer>
inline permuted_range<IndexContainer, ElementRange> operator| (
    ElementRange& r,
    permuted_holder<IndexContainer> i)
{
    return permuted_range<IndexContainer, ElementRange>(i.val, r);
}

template <class ElementRange, class IndexContainer>
inline permuted_range<IndexContainer, const ElementRange> operator| (
    const ElementRange& r,
    permuted_holder<IndexContainer> i)
{
    return permuted_range<IndexContainer, const ElementRange>(i.val, r);
}

static boost::range_detail::forwarder<permuted_holder> permuted =
    boost::range_detail::forwarder<permuted_holder>();

Я предполагаю, что можно внести некоторые улучшения.И permutation_iterator кажется достаточно эффективной основой для этого, но, возможно, есть лучшие альтернативы.Есть мысли?

...