Sequence-zip функция для c ++ 11? - PullRequest
       4

Sequence-zip функция для c ++ 11?

82 голосов
/ 15 декабря 2011

С новым циклом for, основанным на диапазоне, мы можем написать код вроде

for(auto x: Y) {}

Какой ИМО является огромным улучшением от (например)

for(std::vector<int>::iterator x=Y.begin(); x!=Y.end(); ++x) {}

Может ли он использоваться для зацикливания двух одновременных циклов, как функция Pythons zip? Для тех, кто не знаком с Python, код:

Y1 = [1,2,3]
Y2 = [4,5,6,7]
for x1,x2 in zip(Y1,Y2):
    print x1,x2

Дает в качестве вывода (1,4) (2,5) (3,6)

Ответы [ 13 ]

1 голос
/ 29 декабря 2016

Для библиотеки обработки потоков C ++ Я пишу, что искал решение, которое не использует сторонние библиотеки и работает с произвольным числом контейнеров.Я закончил с этим решением.Это похоже на принятое решение, которое использует boost (а также приводит к неопределенному поведению, если длина контейнера не равна)

#include <utility>

namespace impl {

template <typename Iter, typename... Iters>
class zip_iterator {
public:
  using value_type = std::tuple<const typename Iter::value_type&,
                                const typename Iters::value_type&...>;

  zip_iterator(const Iter &head, const Iters&... tail)
      : head_(head), tail_(tail...) { }

  value_type operator*() const {
    return std::tuple_cat(std::tuple<const typename Iter::value_type&>(*head_), *tail_);
  }

  zip_iterator& operator++() {
    ++head_; ++tail_;
    return *this;
  }

  bool operator==(const zip_iterator &rhs) const {
    return head_ == rhs.head_ && tail_ == rhs.tail_;
  }

  bool operator!=(const zip_iterator &rhs) const {
    return !(*this == rhs);
  }

private:
  Iter head_;
  zip_iterator<Iters...> tail_;
};

template <typename Iter>
class zip_iterator<Iter> {
public:
  using value_type = std::tuple<const typename Iter::value_type&>;

  zip_iterator(const Iter &head) : head_(head) { }

  value_type operator*() const {
    return value_type(*head_);
  }

  zip_iterator& operator++() { ++head_; return *this; }

  bool operator==(const zip_iterator &rhs) const { return head_ == rhs.head_; }

  bool operator!=(const zip_iterator &rhs) const { return !(*this == rhs); }

private:
  Iter head_;
};

}  // namespace impl

template <typename Iter>
class seq {
public:
  using iterator = Iter;
  seq(const Iter &begin, const Iter &end) : begin_(begin), end_(end) { }
  iterator begin() const { return begin_; }
  iterator end() const { return end_; }
private:
  Iter begin_, end_;
};

/* WARNING: Undefined behavior if iterator lengths are different.
 */
template <typename... Seqs>
seq<impl::zip_iterator<typename Seqs::iterator...>>
zip(const Seqs&... seqs) {
  return seq<impl::zip_iterator<typename Seqs::iterator...>>(
      impl::zip_iterator<typename Seqs::iterator...>(std::begin(seqs)...),
      impl::zip_iterator<typename Seqs::iterator...>(std::end(seqs)...));
}
0 голосов
/ 21 июня 2016

Вот простая версия, не требующая повышения. Он не будет особенно эффективным, поскольку создает временные значения и не обобщает контейнеры, кроме списков, но не имеет зависимостей и решает наиболее распространенный случай для архивирования.

template<class L, class R>
std::list< std::pair<L,R> >  zip(std::list<L> left, std::list<R> right)
{
auto l = left.begin();
auto r = right.begin();
std::list< std::pair<L,R> > result;
  while( l!=left.end() && r!=right.end() )
    result.push_back( std::pair<L,R>( *(l++), *(r++) ) );
  return result;
}

Хотя другие версии более гибкие, часто смысл использования оператора списка - сделать простой однострочный. Преимущество этой версии заключается в простоте общего случая.

0 голосов
/ 15 декабря 2011

Boost.Iterators имеет zip_iterator, которые вы можете использовать (пример в документации). Он не будет работать с диапазоном, но вы можете использовать std::for_each и лямбду.

...