Существует ли стандартный циклический итератор в C ++ - PullRequest
21 голосов
/ 11 апреля 2010

На основании следующего вопроса: Проверить, является ли одна строка вращением другой строки

Я думал о создании циклического типа итератора, который принимает диапазон и смог бы решить вышеуказанную проблему следующим образом:

std::string s1 = "abc" ;
std::string s2 = "bca" ;
std::size_t n = 2; // number of cycles
cyclic_iterator it(s2.begin(),s2.end(),n);
cyclic_iterator end;

if (std::search(it, end, s1.begin(),s1.end()) != end)
{
   std::cout << "s1 is a rotation of s2" << std::endl;
}

Мой вопрос, уже есть что-то подобное? Я проверил Boost и STL, и ни один из них не имеет точной реализации.

У меня есть простой рукописный (полученный из std::forward_iterator_tag специализированной версии std::iterator) вариант, но я предпочел бы использовать уже созданную / протестированную реализацию.

Ответы [ 6 ]

11 голосов
/ 11 апреля 2010

Ничего подобного в стандарте нет. Циклы плохо работают с итераторами C ++, потому что последовательность, представляющая весь цикл, будет иметь first == last и, следовательно, будет пустой последовательностью.

Возможно, вы могли бы ввести какое-то состояние в итератор, логический флаг для обозначения "еще не сделано". Флаг участвует в сравнении. Установите его true перед итерацией и false при увеличении / уменьшении.

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

РЕДАКТИРОВАТЬ: Теперь я заметил, что вы указали количество циклов. Это имеет большое значение.

template< class I >
class cyclic_iterator
 /* : public iterator< bidirectional, yadda yadda > */ {
    I it, beg, end;
    int cnt;
    cyclic_iterator( int c, I f, I l )
        : it( f ), beg( f ), end( l ), cnt( c ) {}
public:
    cyclic_iterator() : it(), beg(), end(), cnt() {}

    cyclic_iterator &operator++() {
        ++ it;
        if ( it == end ) {
            ++ cnt;
            it = beg;
        }
    } // etc for --, post-operations

    friend bool operator==
        ( cyclic_iterator const &lhs, cyclic_iterator const &rhs )
        { return lhs.it == rhs.it && lhs.cnt == rhs.cnt; } // etc for !=

    friend pair< cyclic_iterator, cyclic_iterator > cycle_range
        ( int c, I f, I l ) {//factory function, better style outside this scope
        return make_pair( cyclic_iterator( 0, f, l ),
                          cyclic_iterator( c, f, l ) );
    }
};
3 голосов
/ 07 февраля 2012

Это должно обеспечить некоторые идеи / решения: 2 представления, второе немного легче по весу. Оба протестированы с использованием поддиапазона вектора и списка ...

#include <vector>

template <typename T, typename Container = std::vector<T>, typename Iterator = Container::iterator>
class RingIterator : public std::iterator <std::bidirectional_iterator_tag, T, ptrdiff_t>
{
  Container& data;

  Iterator   cursor;
  Iterator   begin;
  Iterator   end;

  public:

    RingIterator (Container& v) : data(v), cursor(v.begin()), begin(v.begin()), end(v.end()) {}

    RingIterator (Container& v, const Iterator& i) : data(v), cursor(i), begin(v.begin()), end(v.end()) {}

    RingIterator (Container& v, const Iterator& i, const Iterator& j) : data(v), cursor(i), begin(i), end(j) {}

    RingIterator (Container& v, size_t i) : data(v), cursor(v.begin() + i % v.size()), begin(v.begin()), end(v.end()) {}

    bool operator == (const RingIterator& x) const 
    { 
      return cursor == x.cursor; 
    }

    bool operator != (const RingIterator& x) const 
    {
      return ! (*this == x); 
    }

    reference operator*() const 
    {
      return *cursor; 
    }

    RingIterator& operator++() 
    {
      ++cursor;
      if (cursor == end)
        cursor = begin;
      return *this;
    }

    RingIterator operator++(int) 
    {
      RingIterator ring = *this;
      ++*this;
      return ring;
    }

    RingIterator& operator--() 
    {
      if (cursor == begin)
        cursor = end;
      --cursor;
      return *this;
    }

    RingIterator operator--(int) 
    {
      RingIterator ring = *this;
      --*this; 
      return ring;
    }

    RingIterator insert (const T& x)
    {
      return RingIterator (data, data.insert (cursor, x));
    }

    RingIterator erase() 
    {
      return RingIterator (data, data.erase (cursor));
    }
};

template <typename T, typename Iterator>
class CyclicIterator : public std::iterator <std::bidirectional_iterator_tag, T, ptrdiff_t>
{
  Iterator   cursor;
  Iterator   begin;
  Iterator   end;

  public:

    CyclicIterator (const Iterator& i, const Iterator& j) : cursor(i), begin(i), end(j) {}

    bool operator == (const CyclicIterator& x) const 
    { 
      return cursor == x.cursor; 
    }

    bool operator != (const CyclicIterator& x) const 
    {
      return ! (*this == x); 
    }

    reference operator*() const 
    {
      return *cursor; 
    }

    CyclicIterator& operator++() 
    {
      ++cursor;
      if (cursor == end)
        cursor = begin;
      return *this;
    }

    CyclicIterator operator++(int) 
    {
      CyclicIterator ring = *this;
      ++*this;
      return ring;
    }

    CyclicIterator& operator--() 
    {
      if (cursor == begin)
        cursor = end;
      --cursor;
      return *this;
    }

    CyclicIterator operator--(int) 
    {
      CyclicIterator ring = *this;
      --*this; 
      return ring;
    }
};

#include <iostream>
#include <iomanip>

#include <list>

enum { CycleSize = 9, ContainerSize };

template <typename cyclicIterator>
void test (cyclicIterator& iterator, size_t mn)
{
  int m = mn;
  while (m--)
    for (int n = mn; n--; ++iterator)
      std::cout << std::setw(3) << *iterator << ' ';
  --iterator;
  m = mn;
  while (m--)
    for (int n = mn; n--; --iterator)
      std::cout << std::setw(3) << *iterator << ' ';
}

template <typename containers>
void load (containers& container)
{
  while (container.size() < ContainerSize)
    container.push_back (container.size());
}

void main (void)
{
  typedef std::vector<int>     vContainer;
  typedef vContainer::iterator vIterator;
  typedef std::list<int>       lContainer;
  typedef lContainer::iterator lIterator;

  vContainer v;  load (v);
  vIterator vbegin = v.begin() + 1;

  RingIterator <int, vContainer, vIterator> vring (v, vbegin, v.end());
  CyclicIterator <int, vIterator> vcycle (vbegin, v.end());

  lContainer l;  load (l);
  lIterator lbegin = l.begin(); ++lbegin;

  RingIterator <int, lContainer, lIterator> lring (l, lbegin, l.end());
  CyclicIterator <int, lIterator> lcycle (lbegin, l.end());

  test (vring, CycleSize);
  test (vcycle, CycleSize);
  test (lring, CycleSize);
  test (lcycle, CycleSize);
}
2 голосов
/ 13 августа 2016

Библиотека CGAL определяет Циркуляторы . Они используются как это.

template<class Circulator, class T> 
bool contains(Circulator c, Circulator d, const T& value) { 
  if (c != 0) { 
    do { 
      if (*c == value) 
        return true; 
    } while (++c != d); 
  } 
  return false; 
} 

Обратите внимание, что на первый взгляд они выглядят как итераторы, но обратите внимание, что логика (и структура цикла) отличается от логики итераторов). if(not empty) do{..}while() вместо while(){...}.

0 голосов
/ 04 января 2019

Библиотека диапазонов-v3 Эрика Ниблера (на которой будут базироваться следующие C ++ 20 диапазоны ) имеет ranges::view::cycle. Это адаптирует диапазон источника в бесконечно повторяющийся бесконечный диапазон. Однако нам требуется один повтор, который можно легко выполнить, используя ranges::view::concat.

#include <ranges/v3/all.hpp>

int main() {
    std::string s1 = "abc";
    std::string s2 = "bca";

    auto s2s2 = ranges::view::concat(s2, s2);
    auto i = std::search(s2s2.begin(), s2s2.end(), s1.begin(), s1.end());
    if (i != s2s2.end() && s1.size() == s2.size()) {
        std::cout << "s1 is a rotation of s2\n";
    }
}
0 голосов
/ 03 июня 2015

С другой стороны, сама идея циклического итератора несовместима с идеологией контейнера STL. Вы не должны хотеть циклический итератор, поскольку пользователь этого итератора может быть удивлен его необычным поведением. Обычно в STL вы выполняете итерации от начала до конца контейнера. Бесконечный цикл в этом случае. Потому что конец недостижим.

В конце концов, очевидно, что вы не собираетесь делать более 2 циклов, чтобы решить свою задачу. Нет причин иметь специальный итератор с непонятным поведением. То есть лучше повторять обычный линейный контейнер дважды или даже меньше, чем дважды.

0 голосов
/ 11 апреля 2010

Возможно, вы ищете кольцевой буфер Boost's .Но если вы уже проверили Boost, это может быть не тот, который вам нужен.

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