Разделение диапазона на поддиапазоны - PullRequest
0 голосов
/ 10 ноября 2010

У меня есть контейнер std :: vector, и я хотел бы эффективно разделить его на поддиапазоны с x элементами в каждом.Исходный контейнер не нужен, поэтому элементы следует перемещать и не копировать в поддиапазоны.

Мне удалось выполнить разбиение с помощью копирования, однако я не уверен, как это сделать с помощью назначений перемещения?

    range.insert(range.end(), new_items.begin(), new_items.end());
    while(range.size() >= x)
    {
        sub_ranges.push_back(std::vector<int>(range.begin(), range.begin() + x));
        range = std::vector<int>(range.begin() + x, range.end());
    }

РЕДАКТИРОВАТЬ:

Некоторый прогресс ... все еще не совсем там, и немного некрасиво

    while(range.size() >= x)
    {
        std::vector<short> sub_range(x); // Unnecessary allocation?
        std::move(range.begin(), range.begin() + x, sub_range.begin());
        sub_ranges_.push_back(std::move(sub_range));

        std::move(range.begin() + x, range.end(), range.begin());
        range.resize(range.size() - x);
    }

Ответы [ 2 ]

3 голосов
/ 10 ноября 2010

Один вопрос: вы когда-нибудь слышали о концепции View.

Идея состоит в том, что вместо фактического перемещения данных вы просто создаете «представление» (шаблон Proxy), которое будет ограничивать / изменятьваше восприятие этого.

Например, для диапазона очень простой реализацией будет:

template <typename Iterator>
struct Range
{
  Iterator mBegin, mEnd;
};

Boost.Range предлагает толстую версию со множеством вещей.

Преимущества в этом случае многочисленны.среди которых:

  • Один vector, что обеспечивает лучшую локальность памяти
  • Разделение простое и не требует перемещения / копирования данных

Вот split с этим методом:

typedef Range<std::vector<int>::iterator> range_type;

std::vector<range_type> split(std::vector<int> const& range, size_t x)
{
  std::vector<range_type> subRanges;
  for (std::vector<int>::iterator it = range.begin(), end = range.end();
       it != end; it += std::min(x, (size_t)std::distance(it,end)))
  {
    subRanges.push_back(range_type(it,end));
  }
  return subRanges;
}

Конечно, это работает, только если вы можете держать объект range вокруг.


ОтносительноВаш оригинальный алгоритм: использование цикла while является ложным и заставляет вас использовать гораздо больше move s, чем необходимо.Созданная мной петля for должна быть намного лучше в этом отношении.

1 голос
/ 10 ноября 2010

Вы можете использовать std::make_move_iterator в <iterator>, чтобы превратить ваш итератор в move_iterator.Этот итератор будет std::move результатом разыменования своего базового итератора, что позволит переместить его в другое место.

// assuming I understand your code, which I don't
range.insert(range.end(), new_items.begin(), new_items.end());
while(range.size() >= x)
{
    auto first = std::make_move_iterator(range.begin());
    auto last = std::make_move_iterator(range.begin() + x);

    sub_ranges.push_back(std::vector<int>(first, last));
    range = std::vector<int>(range.begin() + x, range.end());
}

РЕДАКТИРОВАТЬ: И, как вы обнаружили, существует соответствие между std::move() и make_move_iterator:

std::move(first, last, out); // is the same as
std::copy(std::make_move_iterator(first), std::make_move_iterator(last), out);

То, что вы найдете чище, зависит от вас.(Первый для меня.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...