Подтвердить что ты не робот

Цепочные итераторы для С++

Python itertools реализует chain итератор, который по существу объединяет несколько разных итераторов, чтобы обеспечить все, от одного итератора.

Есть ли что-то подобное в С++? Быстрый взгляд на библиотеки ускорения не показал ничего подобного, что для меня удивительно. Трудно ли реализовать эту функциональность?

4b9b3361

Ответ 1

Прошел этот вопрос, исследуя аналогичную проблему.

Даже если вопрос старый, теперь во время С++ 11 и boost 1.54 это довольно легко сделать, используя библиотеку Boost.Range. Он имеет join -функцию, который может объединяться в два диапазона в один. Здесь вы можете понести штрафы за производительность, поскольку самая низкая концепция общего диапазона (т.е. диапазон одиночного прохода или дальность вперед и т.д.) Используется как новая категория диапазона, а во время итерации итератор можно проверить, нужно ли переходить к новому диапазону, но ваш код можно легко написать, например:

#include <boost/range/join.hpp>

#include <iostream>
#include <vector>
#include <deque>

int main()
{
  std::deque<int> deq = {0,1,2,3,4};  
  std::vector<int> vec = {5,6,7,8,9};  

  for(auto i : boost::join(deq,vec))
    std::cout << "i is: " << i << std::endl;

  return 0;
}

Ответ 2

В С++ итератор обычно не имеет смысла вне контекста начала и конца диапазона. Сам итератор не знает, где начинаются и заканчиваются. Итак, чтобы сделать что-то подобное, вам вместо этого нужно объединить диапазоны итераторов - диапазон - это пара (начало, конец) итераторов.

Взгляд на документацию boost:: range. Он может предоставлять инструменты для построения цепочки диапазонов. Единственное отличие состоит в том, что они должны быть одного типа и возвращать одинаковый тип итератора. Возможно, также возможно сделать этот дополнительный родословный для объединения разных типов диапазонов с чем-то вроде any_iterator, но, возможно, и нет.

Ответ 3

Я написал один раньше (на самом деле, просто для объединения двух пар итераторов вместе). Это не так сложно, особенно если вы используете boost iterator_facade.

Создание входного итератора (что фактически делает Python chain) - простой первый шаг. Поиск правильной категории для итератора, связывающего комбинацию разных категорий итераторов, остается как упражнение для читателя; -).

Ответ 4

То, что вы в основном ищете, - это итератор фасада, который абстрагирует прохождение через несколько последовательностей.

Поскольку вы исходите из фона python, я предполагаю, что вам больше нужна гибкость, а не скорость. Благодаря гибкости я имею в виду способность целенаправленно перебирать разные типы последовательностей (вектор, массив, связанный список, набор и т.д.), А по скорости я имею в виду только выделение памяти из стека.

Если это так, вы можете посмотреть на any_iterator из adobe labs: http://stlab.adobe.com/classadobe_1_1any__iterator.html

Этот итератор даст вам возможность итерации по любому типу последовательности во время выполнения. Для цепочки у вас будет вектор (или массив) из 3-х кортежей any_iterators, т.е. Три any_iterators для каждого диапазона, который вы соединяете вместе (вам нужно три, чтобы перебирать вперед или назад, если вы просто хотите перебрать вперед два, будет достаточно).

Предположим, что вы хотите провести целую цепочку целых чисел:

(непроверенный код psuedo-С++)

typedef adobe:: any_iterator AnyIntIter;

struct AnyRange { AnyIntIter начинается; AnyIntIter curr; Конец AnyIntIter; };

Вы можете определить диапазон, например:

int int_array [] = {1, 2, 3, 4}; AnyRange sequence_0 = {int_array, int_array, int_array + ARRAYSIZE (int_array)};

В вашем классе RangeIterator будет std::vector.

<code>
class RangeIterator {
 public:
  RangeIterator() : curr_range_index(0) {}

  template <typename Container>
  void AddAnyRange(Container& c) {
    AnyRange any_range = { c.begin(), c.begin(), c.end() };
    ranges.push_back(any_range);
  }

  // Here what the operator++() looks like, everything else omitted.
  int operator++() {

     while (true) {
       if (curr_range_index > ranges.size()) {
         assert(false, "iterated too far");
         return 0;
       }
       AnyRange* any_range = ranges[curr_range_index];
       if (curr_range->curr != curr_range->end()) {
         ++(curr_range->curr);
         return *(curr_range->curr);
       }
       ++curr_range_index;      
     }
  }


 private:
  std::vector<AnyRange> ranges;
  int curr_range_index; 
};
</code>

Я хочу отметить, однако, что это решение происходит очень медленно. Лучший, более похожий на С++ подход - это просто сохранить все указатели на объекты, на которых вы хотите работать, и повторить их. Кроме того, вы можете применить функтора или посетителя к своим диапазонам.

Ответ 5

Не в стандартной библиотеке. Boost может иметь что-то.

Но на самом деле такая вещь должна быть тривиальной для реализации. Просто сделайте себе итератор с вектором итераторов в качестве участника. Какой-то очень простой код для оператора ++, и вы там.

Ответ 6

Отметьте Библиотека шаблонов просмотров (VTL). Он может не предоставлять "прикованный итератор" напрямую. Но я думаю, что у него есть все необходимые инструменты/шаблоны для реализации вашего собственного "цепного итератора".


На странице VTL:

Вид представляет собой контейнерный адаптер, который предоставляет интерфейс контейнера для

  • части данных или
  • перестановка данных или
  • преобразованные данные или
  • подходящая комбинация наборов данных

базового контейнера (ов). Поскольку сами представления предоставляют интерфейс контейнера, их можно легко комбинировать и складывать. Из-за трюков шаблона представления могут адаптировать свой интерфейс к базовому контейнеру (контейнерам). Более сложный шаблонный трюк делает эту мощную функцию простой в использовании.

По сравнению с интеллектуальными итераторами представления - это просто умные фабрики итераторов.

Ответ 7

Никакой функциональности в boost, которая реализует это, насколько мне известно, я сделал довольно обширный поиск.

Я думал, что на прошлой неделе я бы это легко реализовал, но я столкнулся с проблемой: STL, который поставляется с Visual Studio 2008, когда включена проверка диапазона, не позволяет сравнивать итераторы из разных контейнеров (т.е. вы можете 't сравнить somevec1.end() с somevec2.end()). Внезапно стало намного сложнее реализовать это, и я еще не совсем решил, как это сделать.

Я писал другие итераторы в прошлом, используя iterator_facade и iterator_adapter от boost, которые лучше, чем писать "сырые" итераторы, но я все еще нахожу, что написание пользовательских итераторов на С++ довольно беспорядочно.

Если кто-то может опубликовать некоторый псевдокод о том, как это можно было бы сделать/без/сравнения итераторов из разных контейнеров, я был бы очень обязан.