ฟังก์ชัน Sequence-zip สำหรับ c ++ 11?


104

ด้วย range-based ใหม่สำหรับลูปเราสามารถเขียนโค้ดเช่น

for(auto x: Y) {}

IMO ใดเป็นการปรับปรุงครั้งใหญ่จาก (เช่น)

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

สามารถใช้เพื่อวนซ้ำสองลูปพร้อมกันเช่นzipฟังก์ชันPythons ได้หรือไม่? สำหรับผู้ที่ไม่คุ้นเคยกับ 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)


ตามช่วงforสามารถใช้ได้กับตัวแปรเดียวเท่านั้นจึงไม่ หากคุณต้องการเข้าถึงสองค่าพร้อมกันคุณต้องใช้สิ่งที่ต้องการstd::pair
Seth Carnegie

4
@SethCarnegie: ไม่ใช่โดยตรง แต่คุณสามารถสร้างzip()ฟังก์ชันที่ส่งคืนสิ่งที่เพิ่มขึ้นและทำซ้ำในรายการสิ่งที่เพิ่มขึ้น
André Caron

2
@ AndréCaronคุณพูดถูก "ไม่" ของฉันมีไว้เพื่อบอกว่าคุณไม่สามารถใช้สองตัวแปรไม่ใช่ว่าคุณจะทำซ้ำสองคอนเทนเนอร์พร้อมกันไม่ได้
Seth Carnegie

for(;;)สามารถรับพฤติกรรมนี้ได้อย่างชัดเจนแม้ว่าจะใช้เวลานานคำถามก็จริง: เป็นไปได้ไหมที่ "อัตโนมัติ" มากกว่าสองวัตถุพร้อมกัน?

ในการแก้ไขในอนาคต (หวังว่า C ++ 17) การยกเครื่อง STL จะรวมช่วงต่างๆ จากนั้นดู :: zipอาจให้โซลูชันที่ต้องการ
John McFarlane

คำตอบ:


90

คำเตือน: boost::zip_iteratorและboost::combineเมื่อ Boost 1.63.0 (26 ธันวาคม 2559) จะทำให้เกิดพฤติกรรมที่ไม่ได้กำหนดหากความยาวของคอนเทนเนอร์อินพุตไม่เท่ากัน (อาจผิดพลาดหรือซ้ำเกินจุดสิ้นสุด)


เริ่มต้นจากการเพิ่ม 1.56.0 (7 สิงหาคม 2014) คุณสามารถใช้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 ก 4
5 8 ข 5
6 9 ค 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);
}

การใช้งานก็เหมือนกัน


1
คุณสามารถใช้สิ่งนี้ในการจัดเรียงได้หรือไม่? เช่น std :: sort (zip (a.begin (), ... ), zip (a.end (), ... ), [] (tup a, tup b) {a.get <0> () > b.get <0> ()}); เหรอ?
gnzlbg

@gnzlbg: ไม่มีคุณไม่สามารถ
kennytm

ฉันจะถูกล่อลวงโดยoptionalองค์ประกอบสำหรับความเป็นไปได้ในการทำซ้ำที่ผ่านมา - สุดท้าย
Yakk - Adam Nevraumont

3
มีโอกาสใดบ้างที่คุณสามารถทำได้ด้วย std :: make_tuple และ std :: tie? ฉันพยายามใช้สิ่งนี้ในขณะที่ลดการพึ่งพาการเพิ่ม แต่ฉันไม่สามารถใช้งานได้
Carneiro

@kennytm มีความคิดว่าทำไมพวกเขาถึงตัดสินใจไปกับ UB แทนที่จะจบลงที่จุดสิ้นสุดของช่วงที่สั้นที่สุดในกลุ่ม?
Catskul

21

ดังนั้นฉันจึงเขียน 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());
}

4
คุณควรตรวจสอบว่ามีการวนซ้ำอยู่ที่ส่วนท้ายหรือไม่
Xeo

1
@Xeo ทุกช่วงควรมีขนาดเท่ากับช่วงแรกหรือสูงกว่า
aaronman

คุณสามารถอธิบายวิธีการ[](int i,int j,float k,float l)ทำงานได้หรือไม่? นี่คือฟังก์ชันแลมด้าหรือไม่?
ติดยาเสพติด

@Hooked ใช่มันเป็นแลมบ์ดามันใช้งานได้โดยทั่วไปstd::for_eachแต่คุณสามารถใช้จำนวนช่วงโดยพลการพารามิเตอร์ในแลมบ์ดาขึ้นอยู่กับจำนวนตัวทำซ้ำที่คุณให้ฟังก์ชั่น
aaronman

1
ความต้องการทั่วไปคือการซิปช่วงที่มีขนาดแตกต่างกันหรือแม้กระทั่งช่วงที่ไม่มีที่สิ้นสุด
Xeo

18

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;

หากลำดับที่สองสั้นกว่าการใช้งานของฉันดูเหมือนจะให้ค่าเริ่มต้นที่เป็นค่าเริ่มต้น


1
หากลำดับที่ 2 สั้นกว่าฉันคาดหวังว่านี่คือ UB เหมือนที่คุณจะทำซ้ำในตอนท้ายของb.
Adrian

16

คุณสามารถใช้วิธีแก้ปัญหาตามboost::zip_iterator. สร้างคลาสคอนเทนเนอร์ปลอมโดยดูแลการอ้างอิงไปยังคอนเทนเนอร์ของคุณและที่ส่งคืนzip_iteratorจากฟังก์ชันbeginและendสมาชิก ตอนนี้คุณสามารถเขียน

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

ฉันปล่อยให้เวอร์ชันแปรผันเป็นแบบฝึกหัดที่ยอดเยี่ยมสำหรับผู้อ่าน


3
+1: Boost.Range น่าจะรวมสิ่งนี้ไว้ด้วย อันที่จริงฉันจะส่งคำขอคุณลักษณะนี้ให้พวกเขา
Nicol Bolas

2
@NicolBolas: คุณทำได้ดี สิ่งนี้ควรใช้งานได้ง่ายด้วยboost::iterator_range+ boost::zip_iteratorแม้กระทั่งรุ่นที่แตกต่างกัน
Alexandre C.

1
ฉันเชื่อว่าสิ่งนี้จะไม่มีวันยุติ (และมีพฤติกรรมที่ไม่ได้กำหนดไว้) หากช่วงมีความยาวไม่เท่ากัน
Jonathan Wakely

1
boost::zip_iteratorใช้ไม่ได้กับช่วงที่มีความยาวต่างกัน
Jonathan Wakely

1
สิ่งนี้ควรใช้งานได้แม้ใน c ++ 03 ที่สะอาดพร้อมคู่แทนทูเปิล แต่สิ่งนี้ยังสร้างปัญหาเมื่อความยาวไม่เท่ากัน อาจทำอะไรบางอย่างกับ end () โดยใช้ส่วนท้ายที่สอดคล้องกัน () ของคอนเทนเนอร์ที่เล็กที่สุด สิ่งนี้ดูเหมือนจะอยู่ในข้อมูลจำเพาะเหมือนในคำถาม OPs
พอล

15

ดู<redi/zip.h>สำหรับzipฟังก์ชั่นที่ทำงานร่วมกับช่วงฐานforและยอมรับหมายเลขใด ๆ ของช่วงซึ่งสามารถ rvalues หรือ lvalues และสามารถที่จะมีความยาวแตกต่างกัน (ซ้ำจะหยุดที่จุดสิ้นสุดของช่วงที่สั้นที่สุด)

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


2
คุณยังสามารถใช้boost/tuple/tuple_io.hppในการcout << i;
kirill_igum

นี่คือสิ่งที่ได้ผลสำหรับฉัน อย่างไรก็ตามในรหัสของฉันฉันต้องใช้เทียบเท่า boost::get<0>(i)และboost::get<1>(i). ฉันไม่แน่ใจว่าเหตุใดจึงไม่สามารถปรับตัวอย่างต้นฉบับได้โดยตรงอาจเกี่ยวข้องกับการที่โค้ดของฉันใช้การอ้างอิงไปยังคอนเทนเนอร์อย่างต่อเนื่อง
YitzikC

11

ด้วยrange-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)]


@ einpoklum-reinstateMonica ตอนนี้มันเป็น!
yuyoyuppe

6

ฉันพบคำถามเดียวกันนี้โดยอิสระและไม่ชอบไวยากรณ์ของข้อใดข้างต้น ดังนั้นฉันจึงมีไฟล์ส่วนหัวแบบสั้นที่ทำเช่นเดียวกับการเพิ่ม 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" ขององค์ประกอบ)


นี่มัน! สามารถใช้อาร์กิวเมนต์ตามจำนวนที่กำหนดได้หรือไม่ซึ่งทั้งหมดถูก จำกัด โดยเทมเพลตที่ชาญฉลาดของคุณให้เป็นจำนวน จำกัด
ติดยาเสพติด

ขณะนี้สามารถจัดการคอนเทนเนอร์แบบขนานได้สูงสุด 9 ตู้เท่านั้น นั่นจะเป็นเรื่องง่ายที่จะก้าวไปข้างหน้า แม้ว่ามาโครแบบต่างๆจะอนุญาตให้มาโคร "zipfor" ตัวเดียวจัดการกับพารามิเตอร์ที่แตกต่างกันได้ แต่ก็ยังต้องเขียนโค้ดมาโครแยกกันสำหรับแต่ละมาโคร (เพื่อส่งไปให้) ดูgroups.google.com/forum/?fromgroups=#!topic/comp.std.c/…และstackoverflow.com/questions/15847837/…
cshelton

จัดการข้อโต้แย้งที่มีขนาดต่างกันได้ดีหรือไม่? (ตามที่อธิบายไว้ใน OP)
coyotte508

@ coyotte508 สมมติว่าคอนเทนเนอร์แรกมีจำนวนองค์ประกอบน้อยที่สุด (และละเว้นองค์ประกอบพิเศษในคอนเทนเนอร์อื่น ๆ ) มันจะง่ายต่อการแก้ไขเพื่อไม่ให้สมมติฐานนี้ แต่จะทำให้มันช้าลง (ปัจจุบันไม่ช้าไปกว่าการเขียนด้วยมือ) เมื่อจำนวนองค์ประกอบตรงกัน
cshelton


6

หากคุณชอบตัวดำเนินการมากเกินไปนี่คือความเป็นไปได้สามประการ สองตัวแรกกำลังใช้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;
    }
}

3

สำหรับไลบรารีการประมวลผลสตรีม C ++ฉันกำลังเขียนฉันกำลังมองหาโซลูชันที่ไม่ต้องพึ่งพาไลบรารีของบุคคลที่สามและทำงานร่วมกับคอนเทนเนอร์จำนวนหนึ่งโดยพลการ ฉันจบลงด้วยการแก้ปัญหานี้ คล้ายกับโซลูชันที่ยอมรับซึ่งใช้การเพิ่มประสิทธิภาพ (และส่งผลให้เกิดพฤติกรรมที่ไม่ได้กำหนดหากความยาวของคอนเทนเนอร์ไม่เท่ากัน)

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

1
ลิงค์เสีย ... จะมีประโยชน์ถ้าโพสต์แสดงวิธีการใช้งานเช่น main ()?
javaLover

@javaLover: คุณสามารถใช้มันได้เช่นเดียวกับ cppitertools ในคำตอบของ @ knedlsepp ความแตกต่างที่น่าสังเกตอย่างหนึ่งคือด้วยวิธีการแก้ปัญหาข้างต้นคุณไม่สามารถปรับเปลี่ยนคอนเทนเนอร์ที่อยู่ภายใต้ได้เนื่องจากoperator*for seq::iteratorส่งคืนการstd::tupleอ้างอิงของ const
winnetou

2

หากคุณมีคอมไพเลอร์ที่สอดคล้องกับ C ++ 14 (เช่น gcc5) คุณสามารถใช้ zipcppitertools Ryan 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
}

0

zip_iteratorคุณสามารถใช้Boost.Iterators ได้(ตัวอย่างในเอกสาร) มันใช้ไม่ได้กับช่วงสำหรับ แต่คุณสามารถใช้std::for_eachและแลมด้าได้


ทำไมถึงไม่ทำงานกับ range-based สำหรับ? รวมเข้ากับ Boost.Range และคุณควรตั้งค่า
Xeo

@Xeo: ฉันก็ไม่รู้จักเรนจ์เหมือนกัน ฉันเดาว่าคุณอาจเกี่ยวข้องกับสำเร็จรูปและทำให้มันใช้งานได้ แต่การใช้ IMO for_eachจะยุ่งยากน้อยกว่า
Cat Plus Plus

คุณหมายความว่าสิ่งนี้ไม่ยุ่งยาก: std::for_each(make_zip_iterator(make_tuple(Y1.begin(), Y2.begin())), make_zip_iterator(make_tuple(Y1.end(), Y2.end())), [](const tuple<int, int>& t){printf("%d %d\n", get<0>(t), get<1>(t)); });?
UncleBens

2
ฉันควรเริ่มแคมเปญLambda Does Not Make std :: for_each ที่เป็นประโยชน์ :)
UncleBens

2
@Xeo: นี่น่าจะเป็นคำถามแยกกัน แต่ทำไมโอ้ทำไม ??
UncleBens

-2

นี่คือเวอร์ชันง่ายๆที่ไม่ต้องเพิ่ม มันจะไม่มีประสิทธิภาพโดยเฉพาะอย่างยิ่งเนื่องจากสร้างค่าชั่วคราวและจะไม่สรุปทับคอนเทนเนอร์อื่นนอกเหนือจากรายการ แต่ไม่มีการอ้างอิงและแก้ปัญหาที่พบบ่อยที่สุดสำหรับการบีบอัด

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

แม้ว่าเวอร์ชันอื่น ๆ จะมีความยืดหยุ่นมากกว่า แต่บ่อยครั้งที่จุดประสงค์ของการใช้ตัวดำเนินการรายการคือการสร้างหนึ่งซับ รุ่นนี้มีประโยชน์ที่กรณีทั่วไปเป็นเรื่องง่าย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.