2013-06-17 34 views
13

Tôi muốn viết một cái gì đó tương tự như mã zip python (http://docs.python.org/2/library/functions.html). zip phải có số lượng vectơ khác nhau của các loại khác nhau và nó trả về một vector của bộ dữ liệu, được cắt ngắn theo độ dài của đầu vào ngắn nhất.C++ 11 Mẫu biến thể: trả về tuple từ danh sách biến số vectơ

Ví dụ:

x = [1, 2, 3] 
v = ['a', 'b'] 

Tôi muốn đầu ra là một vector của [ <1, 'a'>, <2, 'b'>]

Làm thế nào để làm điều này trong C++ 11?

+0

Trong ví dụ của bạn không nên các vectơ có cùng độ dài – aaronman

+0

@aaronman no, anh ấy cắt ngắn thành –

+0

không phải là [this] (http://stackoverflow.com/questions/6631782/implementing-a- variadic-zip-chức năng-với-const-chính xác) gần như chính xác những gì bạn đang tìm kiếm? – mfontanini

Trả lời

19

Việc làm này hăm hở và chỉ với sao chép là khá dễ dàng:

#include <vector> 
#include <tuple> 
#include <algorithm> 

template<class... Ts> 
std::vector<std::tuple<Ts...>> zip(std::vector<Ts> const&... vs){ 
    auto lo = std::min({vs.size()...}); 
    std::vector<std::tuple<Ts...>> v; 
    v.reserve(lo); 
    for(unsigned i = 0; i < lo; ++i) 
     v.emplace_back(vs[i]...); 
    return v; 
} 

Live example.

Với chuyển tiếp hoàn hảo và cho phép di chuyển ra khỏi vector, nó trở thành chỉ là một chút phức tạp hơn, chủ yếu là do người giúp đỡ:

#include <vector> 
#include <tuple> 
#include <algorithm> 
#include <type_traits> 

template<class T> 
using Invoke = typename T::type; 

template<class T> 
using Unqualified = Invoke<std::remove_cv<Invoke<std::remove_reference<T>>>>; 

template<class T> 
using ValueType = typename Unqualified<T>::value_type; 

template<class T> 
T const& forward_index(std::vector<T> const& v, unsigned i){ 
    return v[i]; 
} 

template<class T> 
T&& forward_index(std::vector<T>&& v, unsigned i){ 
    return std::move(v[i]); 
} 

template<class... Vs> 
std::vector<std::tuple<ValueType<Vs>...>> zip(Vs&&... vs){ 
    auto lo = std::min({vs.size()...}); 
    std::vector<std::tuple<ValueType<Vs>...>> v; 
    v.reserve(lo); 
    for(unsigned i = 0; i < lo; ++i) 
     v.emplace_back(forward_index(std::forward<Vs>(vs), i)...); 
    return v; 
} 

Live example.

+0

Tôi có thể chỉnh sửa điều này để hoàn toàn chung chung vào ngày mai, nhưng nó sẽ phức tạp hơn một chút sau đó (có thể sử dụng một bộ đếm đơn cho tất cả các thùng chứa thực sự tốt đẹp). – Xeo

+2

Tôi đã tự hỏi về việc sử dụng ngữ nghĩa di chuyển. Có vẻ như hàm mẫu thứ hai 'forward_index' không bao giờ được sử dụng, bởi vì' vs' là một tham chiếu lvalue. Tôi nghĩ rằng nó nên được thay đổi thành 'forward_index (std :: forward (vs), i) ...'? – nosid

+0

@nosid: Erm ... vâng. Ban đầu tôi đã chuyển loại vector một cách rõ ràng, nhưng điều đó biến mất trong quá trình tái cấu trúc. Cảm ơn. – Xeo

2

Điều này sẽ làm những gì bạn muốn. Nó hoạt động cho bất kỳ phạm vi nào trái ngược với chỉ vector.

template <typename Iterator0, typename Iterator1> 
std::vector<std::tuple< 
    typename Iterator0::value_type, 
    typename Iterator1::value_type>> 
zip(
    Iterator0 begin0, Iterator0 end0, 
    Iterator1 begin1, Iterator1 end1) 
{ 
    std::vector<std::tuple< 
     typename Iterator0::value_type, 
     typename Iterator1::value_type>> result; 
    while (begin0 != end0 && begin1 != end1) 
    { 
     result.emplace_back(*begin0, *begin1); 
     ++begin0; 
     ++begin1; 
    } 
    return result; 
} 

Bạn gọi nó như thế này.

std::vector<int> x; 
std::vector<double> y; 
auto xy = zip(x.begin(), x.end(), y.begin(), y.end()); 

Bạn có thể try it out here.

Có thể sửa đổi zip để sử dụng các mẫu có vectơ, để bạn có thể nén bất kỳ số lượng dải ô nào lại với nhau.

Để tiếp tục khớp với tiêu đề <algorithm>, bạn có thể trả lại void và thay vào đó hãy dùng trình lặp đầu ra để ghi kết quả đầu ra.

+0

Thuật toán, trong khi đột phá tại thời điểm đó, không phản ánh các hoạt động tốt nhất hiện hành của C++ 11. – Yakk

+0

@Yakk Hỗ trợ các phạm vi chung thay vì chỉ 'vectơ' không phải là cách thực hành tốt nhất? –

+1

Hỗ trợ các thùng chứa chung (hoặc các đối tượng có thể lặp lại), không nằm trong phạm vi, vì vậy người gọi không phải lặp lại chính mình: một đối tượng có thể lặp lại là đối tượng 'for (auto && x: obj)' hoạt động. Ở trên không phải là xấu, chỉ cần chúng tôi có thể làm tốt hơn bây giờ!Ngoài ra, thuật toán của bạn không hoạt động trên 'int *', là các trình vòng lặp, mở rộng ở trên là biến đổi, không khai thác '-' trên các trình vòng lặp truy cập ngẫu nhiên và' dự trữ' để giảm một nửa chi phí phân bổ bộ nhớ. mã phải nhanh như mã tùy chỉnh), và lấy một trình vòng lặp đầu ra làm cho nhiều tối ưu hóa không thể. – Yakk

5

Chức năng mẫu sau có thể là điểm khởi đầu tốt.

template <typename ...Types> 
auto zip(const std::vector<Types>&... values) 
    -> std::vector<std::tuple<Types...>> 
{ 
    auto size = std::min({ values.size()... }); 
    std::vector<std::tuple<Types...>> result; 
    for (std::size_t i = 0; i != size; ++i) { 
     result.emplace_back(values[i]...); 
    } 
    return result; 
} 
+0

Tôi thích cách chúng ta có 'std :: min ({vecs.size() ...})' và lặp lại ở đó. – Xeo

Các vấn đề liên quan