2015-02-27 24 views
5

Esiste un equivalente al enumerate ciclo gamma a base di pitone in C++? Immagino qualcosa del genere.Esiste un equivalente al ciclo `enumerate` basato su intervallo da python nel moderno C++?

enumerateLoop (auto counter, auto el, container) { 
    charges.at(counter) = el[0]; 
    aa.at(counter) = el[1]; 
} 

Questo può essere fatto con modelli o macro?

Sono consapevole che posso solo usare una vecchia scuola per-loop e iterare fino a raggiungere container.size(). Ma sono interessato a come questo potrebbe essere risolto usando modelli o macro.

EDIT

ho giocato un po 'con iteratori spinta dopo il suggerimento nei commenti. Ho un'altra soluzione funzionante con C++ 14.

template <typename... T> 
auto zip(const 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); 
} 

template <typename T> 
auto enumerate(const T &container) { 
return zip(boost::counting_range(0, static_cast<int>(container.size())), 
container); 
} 

https://gist.github.com/kain88-de/fef962dc1c15437457a8

+0

che cosa è esattamente sbagliato con la "vecchia scuola" per il ciclo che si desidera utilizzare i modelli o brividi * * macro, invece? – Borgleader

+0

Niente di veramente. Sono solo curioso di vedere come potrebbe essere fatto. –

risposta

3

Ho scritto qualcosa per questo un po 'indietro.

In sostanza, è necessario per avvolgere un iteratore e dargli coppia semantica.

AFAIK, non c'è niente di simile costruito nella lingua. E non credo che abbia anche la spinta. Devi praticamente rotolare il tuo.

// Wraps a forward-iterator to produce {value, index} pairs, similar to 
// python's enumerate() 
template <typename Iterator> 
struct EnumerateIterator { 
private: 
    Iterator current; 
    Iterator last; 
    size_t index; 
    bool atEnd; 

public: 
    typedef decltype(*std::declval<Iterator>()) IteratorValue; 
    typedef pair<IteratorValue const&, size_t> value_type; 

    EnumerateIterator() 
    : index(0), atEnd(true) {} 

    EnumerateIterator(Iterator begin, Iterator end) 
    : current(begin), last(end), index(0) { 
    atEnd = current == last; 
    } 

    EnumerateIterator begin() const { 
    return *this; 
    } 

    EnumerateIterator end() const { 
    return EnumerateIterator(); 
    } 

    EnumerateIterator operator++() { 
    if (!atEnd) { 
     ++current; 
     ++index; 

     atEnd = current == last; 
    } 

    return *this; 
    } 

    value_type operator*() const { 
    return {*current, index}; 
    } 

    bool operator==(EnumerateIterator const& rhs) const { 
    return 
     (atEnd && rhs.atEnd) || 
     (!atEnd && !rhs.atEnd && current == rhs.current && last == rhs.last); 
    } 

    bool operator!=(EnumerateIterator const& rhs) const { 
    return !(*this == rhs); 
    } 

    explicit operator bool() const { 
    return !atEnd; 
    } 
}; 

template<typename Iterable> 
EnumerateIterator<decltype(std::declval<Iterable>().begin())> enumerateIterator(Iterable& list) { 
    return EnumerateIterator<decltype(std::declval<Iterable>().begin())>(list.begin(), list.end()); 
} 

template<typename ResultContainer, typename Iterable> 
ResultContainer enumerateConstruct(Iterable&& list) { 
    ResultContainer res; 
    for (auto el : enumerateIterator(list)) 
    res.push_back(move(el)); 

    return res; 
} 
+1

Bello.Penso che sarebbe meglio se 'enumerate' restituisse un intervallo che generava le coppie al volo (invece di costruire un'intera copia del vettore con coppie al suo interno). –

+0

Dovrai ereditare da 'std :: iterator 'o fare qualche' typedef' manuale per essere un iteratore completo. Non necessario per il 'di base per (:)' cicli suppongo. – Yakk

+0

@JosephMansfield 'enumerateIterator' fa quello che chiedi, penso. 'enumerateConstruct' lo appiattisce? – Yakk

1

È inoltre possibile utilizzare più elegantemente gli intervalli auto disponibili dal C++ 11:

int i = 0; 
for (auto& el : container){ 
    charges.at(counter) = el[0]; 
    aa.at(counter) = el[1]; 
    ++i; 
} 

si devono ancora contare il i a mano, però.

+0

Boost zip ranger e conteggio degli iteratori possono fare il conteggio di 'i' a mano andare via, per valori di andare via che coinvolgono qualche codice piuttosto folle. – Yakk

+1

@Yakk: Cool, rendi questa una risposta. Potrebbe essere utile per l'OP. – steffen

+0

@Yakk con boost Posso ottenere una funzione di enumerazione rapida. Grazie per il consiglio. –

4

enumerazione delle variabili multiple è stato un modo di dire dal C. L'unica complicazione è che non è possibile dichiarare entrambe le variabili nella inizializzazione del ciclo for.

int index; 
for (auto p = container.begin(), index = 0; p != container.end(); ++p, ++index) 

Non credo che sia più semplice (o più potente) di quello.

3

C'è un pre soluzione di C++ 11 in impulso a questo: boost.range.indexed. Sfortunatamente non funziona con loop for-loop in C++ 11, solo loop in stile vecchio. Tuttavia, con C++ 17 dovrebbe essere diventare (quasi) facile come in Python usando structured bindings

allora dovrebbe essere possibile implementare qualcosa che funziona in questo modo:

for (auto& [n,x] : enumerate(vec)) x = n; 

Così, un po 'di attesa ancora;)

Problemi correlati