2013-06-17 8 views
13

Python zip (http://docs.python.org/2/library/functions.html) 'a benzer bir şey yazmak istiyorum. zip, farklı türde değişkenlerden oluşan bir vektör türünü almalı ve en kısa girişin uzunluğuna kesilmiş bir vektör kopyasını döndürür.C++ 11 variadic templates: vektörlerin değişken listesinden dönüş tuple

E.g.

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

ben çıkış ben C++ 11 bu nasıl yaparsınız [ <1, 'a'>, <2, 'b'>]

bir vektör olmak istiyorum?

+0

Örneğinizde vektörler aynı uzunlukta olmamalıdır – aaronman

+0

@aaronman no, en kısa sürede kesmeyi belirtir –

+0

Bu değil mi (http://stackoverflow.com/questions/6631782/implementing-a- variadic-zip-function-with-const-doğruluk) tam olarak aradığınız şey nedir? – mfontanini

cevap

19

oldukça kolaydır yardımcılar nedeniyle:

#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

Bunu yarın tamamen jenerik olabilmek için düzenleyebilirim, ancak biraz daha karmaşık olacak (tüm kaplar için sadece tek bir sayaç kullanmak gerçekten güzel). – Xeo

+2

Hareket semantiğinin kullanımını merak ediyordum. İkinci şablon işlevi gibi görünüyor 'forward_index' asla kullanılmaz, çünkü' vs' bir değer referansıdır. Bence forward_index (std :: forward (vs), i) ... 'olarak değiştirilmelidir. – nosid

+0

@nosid: Erm ... evet. Orijinal olarak vektör tipini açıkça geçtim, ancak bu, refactoring sırasında ortadan kayboldu. Teşekkürler. – Xeo

2

İstediğinizi yapmalı. Sadece vector'un aksine herhangi bir aralıkta çalışır.

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öyle diyorsunuz.

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

try it out here.

Değişken şablonlar kullanmak için zip'u değiştirmek muhtemelen olasıdır, böylece aralıkların herhangi birisini bir araya getirebilirsiniz.

<algorithm> üstbilgisini daha fazla eşleştirmek için void döndürebilir ve bunun yerine çıkışın yazılacağı bir çıktı yineleyici alın.

+0

Algoritma, zamanda çığır açarken, mevcut en iyi C++ 11 uygulamalarını yansıtmaz. – Yakk

+0

@Yakk Sadece "vektör" ler yerine genel menzilleri desteklemek en iyi uygulama değil mi? –

+1

Genel kapsayıcıları (veya yinelenen nesneleri), aralıkları değil, arayan kişinin kendini tekrar etmesi gerekmez: yinelenen bir nesne, (auto && x: obj) için çalışır. Yukarıdaki fena değil, şimdi daha iyisini yapabiliriz!Bir kenara göre, algoritmanız 'int *' üzerinde çalışamaz, yineleyici olan, yukarıdakileri değişken bir acıya doğru genişleten, rastgele erişimli yineleyicilerde '' 've bellek ayırma yükünü yarıya indirgemeyen' rezerv '(iyi rezerv) kod, özel kod kadar hızlı olmalıdır) ve bir çıkış yineleyici almak, birçok optimizasyonu imkansız kılar. – Yakk

5

Aşağıdaki şablon işlevi iyi bir başlangıç ​​noktası olabilir. Çoğunlukla,

#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; 
} 
mükemmel yönlendirme ve vektör dışına izin hamle ile

Live example.

, sadece biraz daha karmaşık hale gelir: Bu hevesle ve sadece kopyalama ile yapmak

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

İkimizin de std :: min ({vecs.size() ...}) 'e sahip olmasını ve oraya kadar yinelemesini seviyorum. – Xeo