2012-05-02 19 views
8

Ecco una semplice funzione di cerniera a due container in C++:C++ zip modelli variadic

template <typename A, typename B> 
std::list<std::pair<A, B> > simple_zip(const std::list<A> & lhs, 
             const std::list<B> & rhs) 
{ 
    std::list<std::pair<A, B> > result; 
    for (std::pair<typename std::list<A>::const_iterator, 
       typename std::list<B>::const_iterator> iter 
     = 
     std::pair<typename std::list<A>::const_iterator, 
       typename std::list<B>::const_iterator>(lhs.cbegin(), 
                 rhs.cbegin()); 
     iter.first != lhs.end() && iter.second != rhs.end(); 
     ++iter.first, ++iter.second) 
    { 
    result.push_back(std::pair<A, B>(*iter.first, *iter.second)); 
    } 
    return result; 
} 

Come dovrei estendere questo ad un numero arbitrario di contenitori con i modelli variadic?

mi piacerebbe general_zip ad accettare un tuple di list s (ogni lista può contenere un tipo diverso) e restituire un list di tuple s.

+1

Possibili DUP (che mi è piaciuto comunque!): http://stackoverflow.com/questions/6631782/implementing-a-variadic-zip -function-with-const-correctness – dirkgently

+8

Quella frase incredibilmente lunga per l'inizializzazione potrebbe essere riscritta come 'auto iter = std :: make_pair (lhs.cbegin(), rhs.cbegin())'. La tua dichiarazione è a lungo waaaaay. – mfontanini

+0

@fontanini Fantastico, grazie mille. – user

risposta

13

Sembra che questo dovrebbe funzionare

std::list<std::tuple<>> simple_zip() { 
    return {}; 
} 

template <typename ...T> 
std::list<std::tuple<T...>> simple_zip(std::list<T>... lst) 
{ 
    std::list<std::tuple<T...>> result; 
    for (int i = 0, e = std::min({lst.size()...}); i != e; i++) { 
    result.emplace_back(std::move(lst.front())...); 
    [](...){} ((lst.pop_front(), 0)...); 
    } 
    return result; 
} 

@Potatoswatter avuto il buon (IMO) osservazione che questo può copiare più di necessari quando le liste sono di diverse dimensioni, e che utilizzando solo iteratori volontà essere migliore dato che pop_front fa più del necessario. Penso che il seguente "corregge" l'iteratore al costo di più codice.

template <typename ...T> 
std::list<std::tuple<T...>> simple_zip(std::list<T>... lst) 
{ 
    std::list<std::tuple<T...>> result; 
    struct { 
    void operator()(std::list<std::tuple<T...>> &t, int c, 
      typename std::list<T>::iterator ...it) { 
     if(c == 0) return; 
     t.emplace_back(std::move(*it++)...); 
     (*this)(t, c-1, it...); 
    } 
    } zip; 
    zip(result, std::min({lst.size()...}), lst.begin()...); 
    return result; 
} 

std::list<std::tuple<>> simple_zip() { 
    return {}; 
} 
+0

Suppongo che 'struct {int dummy; } pops [] = {(lst.pop_front(), 0) ...}; 'è forzare la decompressione del parametro? Nel complesso, soluzione molto interessante. +1 –

+0

+1 Wow, questo è incredibilmente elegante! Sono così impressionato e grato. Una piccola modifica: ho ricevuto un errore del compilatore per 'std :: min (lst.size() ...)'; 'std :: min (1,2,3)' non funziona per me, ma 'std :: min ({1,2,3})' fa, quindi ho aggiunto le parentesi graffe. – user

+2

'struct {int dummy; } pops [] = {(lst.pop_front(), 0) ...}; 'Abuso intelligente, dovrò ricordarlo. – ildjarn

4

Questo è un miglioramento incrementale della prima risposta di Johannes. Evita il dummy struct ed evita di copiare l'intero elenco di input (anche se questo non importa molto a meno che una lista sia più corta delle altre). Inoltre l'ho reso generico su tutti i contenitori.

Ma richiede un generatore boilerplate pacco-index, che è piuttosto utile comunque

template< std::size_t n, typename ... acc > 
struct make_index_tuple { 
    typedef typename make_index_tuple< 
     n - 1, 
     std::integral_constant< std::size_t, n - 1 >, acc ... 
    >::type type; 
}; 

template< typename ... acc > 
struct make_index_tuple< 0, acc ... > 
    { typedef std::tuple< acc ... > type; }; 

L'attuazione "reale" è costituito da una funzione lineare che richiede l'uscita dal programma di utilità sopra, e una funzione di interfaccia che mappe pacchetti per tuple.

template< typename ret_t, std::size_t ... indexes, typename lst_tuple > 
ret_t simple_zip(std::tuple< std::integral_constant< std::size_t, indexes > ... >, 
    lst_tuple const &lst) { 
    ret_t ret; 

    auto iters = std::make_tuple(std::get<indexes>(lst).begin() ...); 
    auto ends = std::make_tuple(std::get<indexes>(lst).end() ...); 

    while (std::max<bool>({ std::get<indexes>(iters) 
          == std::get<indexes>(ends) ... }) == false) { 
     ret.emplace_back(* std::get<indexes>(iters) ++ ...); 
    } 
    return ret; 
} 

template< typename ... T > 
std::list< std::tuple< typename T::value_type ... > > 
simple_zip(T const & ... lst) { 
    return simple_zip 
     < std::list< std::tuple< typename T::value_type ... > > > (
     typename make_index_tuple< sizeof ... lst >::type(), 
     std::tie(lst ...) 
    ); 
} 

Per lo meno, questo mette in prospettiva ciò che Johannes ha fatto sembrare facile. Questo è ciò che assomiglia alla maggior parte degli algoritmi dei modelli variadici, perché non c'è modo di memorizzare lo stato di tipo variadico senza tuple e non c'è modo di elaborare una tupla variadica senza un pacchetto di indici o una funzione meta-ricorsiva. (Modifica: ah, ora Johannes ha usato una funzione locale ricorsiva per definire i pacchetti locali per fare tutto ciò senza il tuple. Impressionante ... se riesci a gestire tutta la programmazione funzionale; v))

+2

Buon codice. "std :: max < bool > ({std :: get < indexes > (iters) == std :: get < indexes > (ends) ...}) == false" HAHA è intelligente. –

1

Un'altra versione: un mix di Johannes e Potatoswatter risposta, cercando di ridurre al minimo la quantità di inganno

template <typename C, typename... Its> 
void simple_zip_details(C& c, size_t size, Its... its) 
{ 
    for (int i = 0; i < size; i++) 
     c.emplace_back(std::move(*its++)...); 
} 

template <typename... Ts> 
std::list<std::tuple<Ts...>> simple_zip(std::list<Ts>... lst) 
{ 
    std::list<std::tuple<Ts...>> result; 
    size_t size = std::min({ lst.size()... }); 
    simple_zip_details(result, size, lst.begin()...); 
    return result; 
} 
+0

Con la dichiarazione di non responsabilità che non ho provato (presumo che tu l'abbia fatto), sono molto impressionato dalla brevità del tuo codice. – user

Problemi correlati