IT

두 개 이상의 컨테이너를 동시에 반복하는 가장 좋은 방법은 무엇입니까?

lottoking 2020. 8. 28. 19:41
반응형

두 개 이상의 컨테이너를 동시에 반복하는 가장 좋은 방법은 무엇입니까?


C ++ 11은 컨테이너를 반복하는 여러 방법을 제공합니다. 예를 들면 :

범위 기반 루프

for(auto c : container) fun(c)

std :: for_each

for_each(container.begin(),container.end(),fun)

그러나 같은 크기의 두 개 이상의 컨테이너를 반복하여 다음과 같은 작업을 수행하는 데 권장되는 방법은 무엇입니까?

for(unsigned i = 0; i < containerA.size(); ++i) {
  containerA[i] = containerB[i];
}

파티에 늦게. 그러나 나는 수준을 반복 할 것입니다. 그러나 고전적인 for루프가 아니라 보안 for대한 범위 기반 루프를 사용합니다.

for(unsigned i : indices(containerA)) {
    containerA[i] = containerB[i];
}

indices적용에 대해 (게으른 평가 된) 범위를 반환하는 간단한 래퍼 함수입니다. 구현은 간단하지만 여기에 게시하기에는 너무 길기 때문에 GitHub에서 구현을 사용할 수 있습니다 .

이 코드는 수동 클래식 루프 를 사용하는 것만 큼 ​​효율적for 입니다.

이 패턴이 데이터에서 자주 발생하는 경우 zip두 개의 시퀀스를 구성하고 쌍을 이룬 요소에 해당하는 튜플 범위를 생성하는 다른 패턴을 사용 하는 것이 좋습니다.

for (auto& [a, b] : zip(containerA, containerB)) {
    a = b;
}

의 구현은 독자 zip를위한 연습으로 잡았지만 indices.

(C ++ 17 이전에는 대신 다음을 작성해야합니다.)

for (auto items&& : zip(containerA, containerB))
    get<0>(items) = get<1>(items);

구체적인 예를 들어,

std::copy_n(contB.begin(), contA.size(), contA.begin())

보다 일반적인 경우 zip_iterator에는 루프에서 사용할 수있는 범위 기반의 작은 함수와 함께 Boost.Iterator를 사용할 수 있습니다 . 대부분의 경우 다음과 같이 작동합니다.

template<class... Conts>
auto zip_range(Conts&... conts)
  -> decltype(boost::make_iterator_range(
  boost::make_zip_iterator(boost::make_tuple(conts.begin()...)),
  boost::make_zip_iterator(boost::make_tuple(conts.end()...))))
{
  return {boost::make_zip_iterator(boost::make_tuple(conts.begin()...)),
          boost::make_zip_iterator(boost::make_tuple(conts.end()...))};
}

// ...
for(auto&& t : zip_range(contA, contB))
  std::cout << t.get<0>() << " : " << t.get<1>() << "\n";

라이브 예.

그러나 본격적인들이 제네릭을 위해,은 아마 더 당신 같은 것을 원하는 멤버가없는 배열과 user-정의 형식에 대해 올바르게 작동합니다, begin()/ end()그러나 않는begin/ end자신의 네임 스페이스 기능을. user-가 또한 기능 현관을 const통해 구체적으로 액세스 할 있습니다 zip_c....

그리고 나처럼 멋진 오류 메시지를 옹호 하는 사람이라면 임시 컨테이너가 함수에 전달을 확인하고 멋진 오류 메시지를 출력하는 것을 원할 것입니다 zip_....


왜 아무도 없습니까?

auto ItA = VectorA.begin();
auto ItB = VectorB.begin();

while(ItA != VectorA.end() || ItB != VectorB.end())
{
    if(ItA != VectorA.end())
    {
        ++ItA;
    }
    if(ItB != VectorB.end())
    {
        ++ItB;
    }
}

추신 : 컨테이너 크기가 일치하지 않는 경우에 문 안에 코드를 넣어야합니다.


에 헤더 제공된대로 여러 컨테이너를 사용하여 특정 작업 을 수행하는 방법 에는 여러 가지algorithm있습니다. 예를 들어, 여러분이 제공하는 한 예제 std::copy에서 명시적인 for 루프 대신 사용할 수 있습니다 .

반면에 루프 이외의 여러 컨테이너를 일반적으로 반복하는 기본 제공 방법은 없습니다. 때문에 이것은 놀라운 있기 일이 아니다 많은 반복하는 방법은. 생각해 봐. 한 컨테이너를 한 컨테이너를 다른 단계로 반복 할 수 있습니다. 또는 한 용기를 끝까지 통과 한 다음 다른 용기의 끝까지 중단하는 동안 삽입을 시작합니다. 또는 다른 용기를 완전히 통과 한 다음 다시 시작할 때마다 첫 번째 단계의 용기의 한 단계; 또는 다른 패턴; 또는 한 번에 두 개 이상의 용기; 기타 ...

그러나 가장 짧은 컨테이너 길이까지 두 ​​개의 컨테이너를 반복하는 고유 한 "for_each"스타일 함수 를 만들고 다음과 같이 할 수 있습니다.

template <typename Container1, typename Container2>
void custom_for_each(
  Container1 &c1,
  Container2 &c2,
  std::function<void(Container1::iterator &it1, Container2::iterator &it2)> f)
{
  Container1::iterator begin1 = c1.begin();
  Container2::iterator begin2 = c2.begin();
  Container1::iterator end1 = c1.end();
  Container2::iterator end2 = c2.end();
  Container1::iterator i1;
  Container1::iterator i2;
  for (i1 = begin1, i2 = begin2; (i1 != end1) && (i2 != end2); ++it1, ++i2) {
    f(i1, i2);
  }
}

어느 쪽이든 방식으로 원하는 모든 종류의 반복 전략을 만들 수 있습니다.

루프를 직접 수행하는 것이 이와 같은 사용자 지정 함수를 물론 작성하는 것보다 더 많은 것을 주장 할 수 있습니다. 그리고 한두 번만 수행 할 경우 옳습니다. 그러나 좋은 점은 상당히 많은 것이 가능하다는 것입니다. =)


2 개의 컨테이너에서 동시에 반복해야하는 경우 부스트 범위 라이브러리에 표준 for_each 알고리즘의 확장 버전이 있습니다. 예 :

#include <vector>
#include <boost/assign/list_of.hpp>
#include <boost/bind.hpp>
#include <boost/range/algorithm_ext/for_each.hpp>

void foo(int a, int& b)
{
    b = a + 1;
}

int main()
{
    std::vector<int> contA = boost::assign::list_of(4)(3)(5)(2);
    std::vector<int> contB(contA.size(), 0);

    boost::for_each(contA, contB, boost::bind(&foo, _1, _2));
    // contB will be now 5,4,6,3
    //...
    return 0;
}

하나의 알고리즘에서 2 개 이상의 컨테이너를 처리해야하는 경우 zip을 사용합니다.


또 다른 솔루션은 람다에서 다른 컨테이너의 반복기 참조를 캡처하고 그에 대해 사후 증분 연산자를 사용하는 것입니다. 예를 들어 간단한 사본은 다음과 같습니다.

vector<double> a{1, 2, 3};
vector<double> b(3);

auto ita = a.begin();
for_each(b.begin(), b.end(), [&ita](auto &itb) { itb = *ita++; })

람다 내부에서 무엇이든 할 수 있으며 ita증가시킬 수 있습니다. 이것은 여러 용기 케이스로 쉽게 확장됩니다.


범위 라이브러리는이 기능과 기타 매우 유용한 기능을 제공합니다. 다음 예제에서는 Boost.Range 를 사용합니다 . Eric Niebler의 rangev3 는 좋은 대안이 될 것입니다.

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

int main(int, const char*[])
{
    std::vector<int> const v{0,1,2,3,4};
    std::list<char> const  l{'a', 'b', 'c', 'd', 'e'};

    for(auto const& i: boost::combine(v, l))
    {
        int ti;
        char tc;
        boost::tie(ti,tc) = i;
        std::cout << '(' << ti << ',' << tc << ')' << '\n';
    }

    return 0;
}

C ++ 17은 구조화 된 바인딩을 사용하여이를 더욱 향상시킵니다.

int main(int, const char*[])
{
    std::vector<int> const v{0,1,2,3,4};
    std::list<char> const  l{'a', 'b', 'c', 'd', 'e'};

    for(auto const& [ti, tc]: boost::combine(v, l))
    {
        std::cout << '(' << ti << ',' << tc << ')' << '\n';
    }

    return 0;
}

다음은 하나의 변형입니다.

template<class ... Iterator>
void increment_dummy(Iterator ... i)
    {}

template<class Function,class ... Iterator>
void for_each_combined(size_t N,Function&& fun,Iterator... iter)
    {
    while(N!=0)
        {
        fun(*iter...);
        increment_dummy(++iter...);
        --N;
        }
    }

사용 예

void arrays_mix(size_t N,const float* x,const float* y,float* z)
    {
    for_each_combined(N,[](float x,float y,float& z){z=x+y;},x,y,z);    
    }

나도 조금 늦었어요. 그러나 이것을 사용할 수 있습니다 (C 스타일 가변 함수) :

template<typename T>
void foreach(std::function<void(T)> callback, int count...) {
    va_list args;
    va_start(args, count);

    for (int i = 0; i < count; i++) {
        std::vector<T> v = va_arg(args, std::vector<T>);
        std::for_each(v.begin(), v.end(), callback);
    }

    va_end(args);
}

foreach<int>([](const int &i) {
    // do something here
}, 6, vecA, vecB, vecC, vecD, vecE, vecF);

또는 이것 (함수 매개 변수 팩 사용) :

template<typename Func, typename T>
void foreach(Func callback, std::vector<T> &v) {
    std::for_each(v.begin(), v.end(), callback);
}

template<typename Func, typename T, typename... Args>
void foreach(Func callback, std::vector<T> &v, Args... args) {
    std::for_each(v.begin(), v.end(), callback);
    return foreach(callback, args...);
}

foreach([](const int &i){
    // do something here
}, vecA, vecB, vecC, vecD, vecE, vecF);

또는 (중괄호로 묶인 이니셜 라이저 목록 사용) :

template<typename Func, typename T>
void foreach(Func callback, std::initializer_list<std::vector<T>> list) {
    for (auto &vec : list) {
        std::for_each(vec.begin(), vec.end(), callback);
    }
}

foreach([](const int &i){
    // do something here
}, {vecA, vecB, vecC, vecD, vecE, vecF});

또는 다음과 같이 벡터를 결합 할 수 있습니다. 두 벡터를 결합하는 가장 좋은 방법은 무엇입니까? 그런 다음 큰 벡터를 반복합니다.

참고 URL : https://stackoverflow.com/questions/12552277/whats-the-best-way-to-iterate-over-two-or-more-containers-simultaneously

반응형