Цепные итераторы для C ++ - PullRequest
24 голосов
/ 11 июня 2009

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

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

Ответы [ 7 ]

20 голосов
/ 07 июля 2013

Наткнулся на этот вопрос при поиске аналогичной проблемы.

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

#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;
}
4 голосов
/ 11 июня 2009

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

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

2 голосов
/ 05 августа 2009

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

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

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

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

Допустим, вы хотели пройти по цепочке через последовательность целых чисел:

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

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's 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>

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

2 голосов
/ 11 июня 2009

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

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

1 голос
/ 11 июня 2009

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


со страницы VTL:

Представление - это адаптер контейнера, который обеспечивает интерфейс контейнера для

  1. части данных или
  2. перестановка данных или
  3. преобразованные данные или
  4. подходящая комбинация наборов данных

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

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

1 голос
/ 11 июня 2009

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

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

0 голосов
/ 11 июня 2009

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

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

В прошлом я писал другие итераторы, используя iterator_facade и iterator_adapter из boost, которые лучше, чем написание «сырых» итераторов, но я все еще нахожу написание пользовательских итераторов в C ++ довольно грязным.

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

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