简体   繁体   English

在C ++中是否有标准的循环迭代器

[英]Is there a standard cyclic iterator in C++

Based on the following question: Check if one string is a rotation of other string 基于以下问题: 检查一个字符串是否是其他字符串的旋转

I was thinking of making a cyclic iterator type that takes a range, and would be able to solve the above problem like so: 我正在考虑制作一个带有范围的循环迭代器类型,并且能够像这样解决上述问题:

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;
}

My question, Is there already something like this available? 我的问题,是否已经有这样的东西? I've checked Boost and STL and neither have an exact implementation. 我检查了Boost和STL,但都没有确切的实现。

I've got a simple hand-written (derived from a std::forward_iterator_tag specialised version of std::iterator ) one but would rather use an already made/tested implementation. 我有一个简单的手写(从std::forward_iterator_tag专门版本的std::iterator派生),但我宁愿使用已经制作/测试过的实现。

There is nothing like this in the standard. 标准中没有这样的东西。 Cycles don't play well with C++ iterators because a sequence representing the entire cycle would have first == last and hence be the empty sequence. 循环不能很好地与C ++迭代器一起使用,因为表示整个循环的序列first == last ,因此是空序列。

Possibly you could introduce some state into the iterator, a Boolean flag to represent "not done yet." 可能你可以在迭代器中引入一些状态,一个布尔标志来表示“尚未完成”。 The flag participates in comparison. 国旗参与比较。 Set it true before iterating and to false upon increment/decrement. 在迭代之前将其设置为true ,在递增/递减时将其设置为false

But it might just be better to manually write the algorithms you need. 但手动编写所需的算法可能更好。 Once you've managed to represent the whole cycle, representing an empty sequence might have become impossible. 一旦你设法代表整个周期,代表一个空序列可能已经变得不可能了。

EDIT: Now I notice that you specified the number of cycles. 编辑:现在我注意到你指定了循环次数。 That makes a big difference. 这有很大的不同。

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 ) );
    }
};

This should provide some ideas/solutions: 2 renditions, the second is a little lighter in weight. 这应该提供一些想法/解决方案:2个再现,第二个是重量轻一点。 Both tested using a subrange of a vector and a list ... 两者都使用向量和列表的子范围进行测试...

#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);
}

The CGAL library defines Circulators . CGAL库定义了循环器 They are used like this. 它们就像这样使用。

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; 
} 

Note that they look like iterators at first glance but note that the logic (and the structure of the loop) is different than for iterators). 请注意,它们看起来像迭代器,但请注意逻辑(和循环的结构)与迭代器不同)。 if(not empty) do{..}while() instead of while(){...} . if(not empty) do{..}while()而不是while(){...}

You're maybe looking for Boost's Circular Buffer . 你可能正在寻找Boost的循环缓冲区 But if you've already checked Boost, it might not be the one you want. 但是如果你已经检查过Boost,它可能不是你想要的那个。

On the other hand, the very idea of cyclic iterator is not compatible to STL container ideology. 另一方面,循环迭代器的想法与STL容器意识形态不兼容。 You should not want cyclic iterator, as the user of this iterator may be surprized by its unusual behavior. 你不应该想要循环迭代器,因为这个迭代器的用户可能会因其不寻常的行为而感到惊讶。 Usually in STL you are iterating from the beginning to the end of container. 通常在STL中,您从容器的开头到结尾进行迭代。 Infinite loop in that case. 在这种情况下无限循环。 Because the end is not reachable. 因为无法到达终点。

After all, obviously, you are not going to do more than 2 cycles to solve your task. 毕竟,显然,你不会做超过2个周期来解决你的任务。 No reason to have special iterator with confusing behavior. 没有理由让具有混乱行为的特殊迭代器。 That is better to iterate usual linear container twice or may be even less then twice. 最好迭代通常的线性容器两次,或者甚至可能少于两次。

Eric Niebler's ranges-v3 library (on which the upcoming C++20 ranges is based) has ranges::view::cycle . Eric Niebler的range-v3库(即将推出的C ++ 20范围基于)具有ranges::view::cycle This adapts its source range into an endlessly repeating infinite range. 这使其源范围适应无限重复的无限范围。 However we require a single repeat which may be easily achieved using ranges::view::concat . 但是我们需要使用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";
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM