program tip

C ++ 11 용 Sequence-zip 함수?

radiobox 2020. 9. 14. 08:17
반응형

C ++ 11 용 Sequence-zip 함수?


새로운 범위 기반 for 루프를 사용하면 다음과 같은 코드를 작성할 수 있습니다.

for(auto x: Y) {}

어떤 IMO가 (예를 들어) 크게 개선되었습니다.

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)


주의 : boost::zip_iteratorboost::combine상기 입력 용기의 길이가 동일하지 않으면 정의되지 않은 동작이 발생할 부스트 1.63.0 (2,016 십이 26 개)의 등 (그 끝을 넘어 충돌이나 반복 처리 할 수있다).


Boost 1.56.0 (2014 년 8 월 7 일)부터 사용할boost::combine있습니다 (이 기능은 이전 버전에 있지만 문서화되지 않음).

#include <boost/range/combine.hpp>
#include <vector>
#include <list>
#include <string>

int main() {
    std::vector<int> a {4, 5, 6};
    double b[] = {7, 8, 9};
    std::list<std::string> c {"a", "b", "c"};
    for (auto tup : boost::combine(a, b, c, a)) {    // <---
        int x, w;
        double y;
        std::string z;
        boost::tie(x, y, z, w) = tup;
        printf("%d %g %s %d\n", x, y, z.c_str(), w);
    }
}

이것은 인쇄됩니다

4 7 a 4
5 8 b 5
6 9 c 6

이전 버전에서는 다음과 같이 범위를 직접 정의 할 수 있습니다.

#include <boost/iterator/zip_iterator.hpp>
#include <boost/range.hpp>

template <typename... T>
auto zip(T&&... containers) -> boost::iterator_range<boost::zip_iterator<decltype(boost::make_tuple(std::begin(containers)...))>>
{
    auto zip_begin = boost::make_zip_iterator(boost::make_tuple(std::begin(containers)...));
    auto zip_end = boost::make_zip_iterator(boost::make_tuple(std::end(containers)...));
    return boost::make_iterator_range(zip_begin, zip_end);
}

사용법은 동일합니다.


.NET 기반 솔루션을 사용할 수 있습니다 boost::zip_iterator. 컨테이너에 대한 참조를 유지 zip_iterator하고 beginend멤버 함수 에서 반환되는 가짜 컨테이너 클래스를 만듭니다 . 이제 쓸 수 있습니다

for (auto p: zip(c1, c2)) { ... }

구현 예 (테스트하십시오) :

#include <iterator>
#include <boost/iterator/zip_iterator.hpp>

template <typename C1, typename C2>
class zip_container
{
    C1* c1; C2* c2;

    typedef boost::tuple<
        decltype(std::begin(*c1)), 
        decltype(std::begin(*c2))
    > tuple;

public:
    zip_container(C1& c1, C2& c2) : c1(&c1), c2(&c2) {}

    typedef boost::zip_iterator<tuple> iterator;

    iterator begin() const
    {
         return iterator(std::begin(*c1), std::begin(*c2));
    }

    iterator end() const
    {
         return iterator(std::end(*c1), std::end(*c2));
    }
};

template <typename C1, typename C2>
zip_container<C1, C2> zip(C1& c1, C2& c2)
{
    return zip_container<C1, C2>(c1, c2);
}

나는 가변 버전을 독자에게 훌륭한 연습으로 남겨둔다.


range-base 함께 작동하고 rvalue 또는 lvalue 일 수 있고 다른 길이 일 수있는 범위의 수를 허용 <redi/zip.h>하는 zip함수를 참조하십시오 for(반복은 최단 범위의 끝에서 중지됨).

std::vector<int> vi{ 0, 2, 4 };
std::vector<std::string> vs{ "1", "3", "5", "7" };
for (auto i : redi::zip(vi, vs))
  std::cout << i.get<0>() << ' ' << i.get<1>() << ' ';

인쇄물 0 1 2 3 4 5


그래서 지루할 때 이전에이 zip을 썼는데, 부스트를 사용하지 않고 C ++ stdlib와 비슷하다는 점에서 다른 것과 다르기 때문에 게시하기로 결정했습니다.

template <typename Iterator>
    void advance_all (Iterator & iterator) {
        ++iterator;
    }
template <typename Iterator, typename ... Iterators>
    void advance_all (Iterator & iterator, Iterators& ... iterators) {
        ++iterator;
        advance_all(iterators...);
    } 
template <typename Function, typename Iterator, typename ... Iterators>
    Function zip (Function func, Iterator begin, 
            Iterator end, 
            Iterators ... iterators)
    {
        for(;begin != end; ++begin, advance_all(iterators...))
            func(*begin, *(iterators)... );
        //could also make this a tuple
        return func;
    }

사용 예 :

int main () {
    std::vector<int> v1{1,2,3};
    std::vector<int> v2{3,2,1};
    std::vector<float> v3{1.2,2.4,9.0};
    std::vector<float> v4{1.2,2.4,9.0};
     zip (
            [](int i,int j,float k,float l){
                std::cout << i << " " << j << " " << k << " " << l << std::endl;
            },
            v1.begin(),v1.end(),v2.begin(),v3.begin(),v4.begin());
}

범위-V3 :

#include <range/v3/all.hpp>
#include <vector>
#include <iostream>

namespace ranges {
    template <class T, class U>
    std::ostream& operator << (std::ostream& os, common_pair<T, U> const& p)
    {
      return os << '(' << p.first << ", " << p.second << ')';
    }
}

using namespace ranges::v3;

int main()
{
    std::vector<int> a {4, 5, 6};
    double b[] = {7, 8, 9};
    std::cout << view::zip(a, b) << std::endl; 
}

출력 :

[(4, 7), (5, 8), (6, 9)]


std :: transform 은 이것을 간단하게 할 수 있습니다 :

std::vector<int> a = {1,2,3,4,5};
std::vector<int> b = {1,2,3,4,5};
std::vector<int>c;
std::transform(a.begin(),a.end(), b.begin(),
               std::back_inserter(c),
               [](const auto& aa, const auto& bb)
               {
                   return aa*bb;
               });
for(auto cc:c)
    std::cout<<cc<<std::endl;

두 번째 시퀀스가 ​​더 짧으면 내 구현이 기본 초기화 값을 제공하는 것 같습니다.


나는이 같은 질문을 독립적으로 만났고 위의 구문이 마음에 들지 않았습니다. 따라서 기본적으로 boost zip_iterator와 동일한 작업을 수행하지만 구문을 더 맛있게 만드는 몇 가지 매크로가있는 짧은 헤더 파일이 있습니다.

https://github.com/cshelton/zipfor

예를 들어 할 수 있습니다.

vector<int> a {1,2,3};
array<string,3> b {"hello","there","coders"};

zipfor(i,s eachin a,b)
    cout << i << " => " << s << endl;

주요 구문 설탕은 각 컨테이너에서 요소의 이름을 지정할 수 있다는 것입니다. 또한 동일한 작업을 수행하는 "mapfor"를 포함하지만 맵 (요소의 ".first"및 ".second"이름 지정)을위한 것입니다.


// declare a, b
BOOST_FOREACH(boost::tie(a, b), boost::combine(list_of_a, list_of_b)){
    // your code here.
}

연산자 오버로딩을 좋아한다면 여기에 세 가지 가능성이 있습니다. 처음 두 사용 std::pair<>std::tuple<>반복자로서, 각각; 세 번째는 이것을 범위 기반으로 확장합니다 for. 모든 사람이 이러한 연산자 정의를 좋아하는 것은 아니므로이를 별도의 네임 스페이스에 유지하고이를 사용 using namespace하려는 함수 (파일이 아님!)에있는 것이 가장 좋습니다.

#include <iostream>
#include <utility>
#include <vector>
#include <tuple>

// put these in namespaces so we don't pollute global
namespace pair_iterators
{
    template<typename T1, typename T2>
    std::pair<T1, T2> operator++(std::pair<T1, T2>& it)
    {
        ++it.first;
        ++it.second;
        return it;
    }
}

namespace tuple_iterators
{
    // you might want to make this generic (via param pack)
    template<typename T1, typename T2, typename T3>
    auto operator++(std::tuple<T1, T2, T3>& it)
    {
        ++( std::get<0>( it ) );
        ++( std::get<1>( it ) );
        ++( std::get<2>( it ) );
        return it;
    }

    template<typename T1, typename T2, typename T3>
    auto operator*(const std::tuple<T1, T2, T3>& it)
    {
        return std::tie( *( std::get<0>( it ) ),
                         *( std::get<1>( it ) ),
                         *( std::get<2>( it ) ) );
    }

    // needed due to ADL-only lookup
    template<typename... Args>
    struct tuple_c
    {
        std::tuple<Args...> containers;
    };

    template<typename... Args>
    auto tie_c( const Args&... args )
    {
        tuple_c<Args...> ret = { std::tie(args...) };
        return ret;
    }

    template<typename T1, typename T2, typename T3>
    auto begin( const tuple_c<T1, T2, T3>& c )
    {
        return std::make_tuple( std::get<0>( c.containers ).begin(),
                                std::get<1>( c.containers ).begin(),
                                std::get<2>( c.containers ).begin() );
    }

    template<typename T1, typename T2, typename T3>
    auto end( const tuple_c<T1, T2, T3>& c )
    {
        return std::make_tuple( std::get<0>( c.containers ).end(),
                                std::get<1>( c.containers ).end(),
                                std::get<2>( c.containers ).end() );
    }

    // implement cbegin(), cend() as needed
}

int main()
{
    using namespace pair_iterators;
    using namespace tuple_iterators;

    std::vector<double> ds = { 0.0, 0.1, 0.2 };
    std::vector<int   > is = {   1,   2,   3 };
    std::vector<char  > cs = { 'a', 'b', 'c' };

    // classical, iterator-style using pairs
    for( auto its  = std::make_pair(ds.begin(), is.begin()),
              end  = std::make_pair(ds.end(),   is.end()  ); its != end; ++its )
    {
        std::cout << "1. " << *(its.first ) + *(its.second) << " " << std::endl;
    }

    // classical, iterator-style using tuples
    for( auto its  = std::make_tuple(ds.begin(), is.begin(), cs.begin()),
              end  = std::make_tuple(ds.end(),   is.end(),   cs.end()  ); its != end; ++its )
    {
        std::cout << "2. " << *(std::get<0>(its)) + *(std::get<1>(its)) << " "
                           << *(std::get<2>(its)) << " " << std::endl;
    }

    // range for using tuples
    for( const auto& d_i_c : tie_c( ds, is, cs ) )
    {
        std::cout << "3. " << std::get<0>(d_i_c) + std::get<1>(d_i_c) << " "
                           << std::get<2>(d_i_c) << " " << std::endl;
    }
}

C ++ 14 호환 컴파일러 (예 : gcc5) zip가있는 경우 cppitertoolsRyan Haining 라이브러리 에서 제공 한 것을 사용할 수 있습니다 . 정말 유망 해 보입니다.

array<int,4> i{{1,2,3,4}};
vector<float> f{1.2,1.4,12.3,4.5,9.9};
vector<string> s{"i","like","apples","alot","dude"};
array<double,5> d{{1.2,1.2,1.2,1.2,1.2}};

for (auto&& e : zip(i,f,s,d)) {
    cout << std::get<0>(e) << ' '
         << std::get<1>(e) << ' '
         << std::get<2>(e) << ' '
         << std::get<3>(e) << '\n';
    std::get<1>(e)=2.2f; // modifies the underlying 'f' array
}

For a C++ stream processing library I'm writing I was looking for a solution that doesn't rely on third party libraries and works with an arbitrary number of containers. I ended up with this solution. It's similar to the accepted solution which uses boost (and also results in undefined behavior if the container lengths are not equal)

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

Boost.Iterators has zip_iterator you can use (example's in the docs). It won't work with range for, but you can use std::for_each and a lambda.


Here is a simple version that does not require boost. It won't be particularly efficient as it creates temporary values, and it does not generalise over containers other than lists, but it has no dependencies and it solves the most common case for zipping.

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

Although the other versions are more flexible, often the point of using a list operator is make a simple one-liner. This version has the benefit that the common-case is simple.

참고URL : https://stackoverflow.com/questions/8511035/sequence-zip-function-for-c11

반응형