2009-10-31 5 views
0

Mon problème est plus complexe que cela. J'ai donc réduit mon problème à un exemple très simple qui me montrerait assez pour savoir comment gérer le reste.Transformation de plusieurs éléments de l'itérateur

Dites que j'ai un itérateur d'entrée. Je veux en faire un nouvel itérateur d'entrée, où chaque élément est la combinaison de plusieurs éléments séquentiels de l'entrée originale avec le motif suivant. La longueur d'exécution est codée dans la séquence d'entrée.

entrée: { 1 1 2 3 4 4 6 7 8 9 ... }

sortie: { (1) (3+4) (6+7+8+9) ... }

Je pensais une fonction de ce type peut traiter un élément unique et incrémenter l'entrée commencent itérateur (transmis par référence). Il y a quelques questions dans mes commentaires, plus j'aimerais savoir s'il y a un bon moyen de le faire pour l'ensemble du flux d'éléments.

EDIT: Je suis conscient qu'il ya un bug dans l'appel à std::advance où le tmp iterator est incrémenté d'être end exactement, ce qui serait valable pour ce code. Concentrons-nous sur le reste de mes questions et je réglerai cela. Modifier 2: devrait être réparé maintenant?

template<class TInputIterator, class TOutputIterator> 
void process_single(TInputIterator& begin, TInputIterator end, TOutputIterator destination) 
{ 
    std::iterator_traits<TInputIterator>::value_type run_length = *begin; 
    ++begin; 

    // is there a better way to specify run_length elements to accumulate() without having to call advance() here? 
    TInputIterator tmp(begin); 
    std::advance(tmp, run_length); 
    // Edited: this condition should work for the different kinds of iterators? 
    if ((end < tmp) || (std::distance(begin, tmp) != run_length)) 
     throw std::range_error("The input sequence had too few elements."); 

    // std::plus is the default accumulate function 
    *destination = std::accumulate(begin, tmp, 0/*, std::plus<TInputIterator::value_type>()*/); 

    // should I use std::swap(begin, tmp) here instead? 
    begin = tmp; 
} 

Edit 3: En réponse aux réponses, serait-ce mieux?

template<class TInputIterator, class TOutputIterator> 
TInputIterator process_single(TInputIterator begin, TInputIterator end, TOutputIterator destination) 
{ 
    typedef std::iterator_traits<TInputIterator>::value_type value_type; 

    value_type run_length = *begin; 
    ++begin; 

    value_type sum = 0; 
    while (run_length > 0 && begin != end) 
    { 
     sum += *begin; 
     ++begin; 
     --run_length; 
    } 

    if (run_length) 
    { 
     throw std::range_error("The input sequence had too few elements."); 
    } 

    *destination = sum; 

    return begin; 
} 

template<class TInputIterator, class TOutputIterator> 
void process(TInputIterator begin, TInputIterator end, TOutputIterator destination) 
{ 
    while (begin != end) 
    { 
     begin = process_single(begin, end, destination); 
    } 
} 
+1

Je suis honnêtement un peu confus ce que la question est ici .. –

Répondre

0
// is there a better way to specify run_length elements to accumulate() without having to call advance() here? 

Pas vraiment.

// Edited: this condition should work for the different kinds of iterators? 
if ((end < tmp) || (std::distance(begin, tmp) != run_length)) 
    throw std::range_error("The input sequence had too few elements."); 

Le problème ici est l'opérateur <, qui ne va travailler pour RandomAccessIterators. Pourquoi ne pas simplement:

if (std::distance(tmp, end) < run_length) 

?

// should I use std::swap(begin, tmp) here instead? 
begin = tmp; 

Nope. L'incrémentation à la fin est un comportement standard pour les algorithmes LIST.

void process_single(TInputIterator& begin, TInputIterator end, TOutputIterator destination) 

Les itérateurs STL ne sont généralement pas un bon type à passer par ré-erreur. Les appelants veulent trop souvent les préserver après l'appel à votre fonction. Par exemple, en passant BYREF provoque ce pas compiler:

std::vector<something> t; 
std::vector<something> t2; 
process_single(t.begin(), t.end(), std::back_inserter(t2)) 

(De nombreux compilateurs prendront, mais ce n'est pas standard)

mieux serait de passer le iterator byval puis retourner la nouvelle position à laquelle vous terminez votre algorithme, pour être plus cohérent avec le reste de la STL. Par exemple, voir std :: find().

J'espère que ça aide ....

2

J'écrirais cet algorithme manuellement.

Tout d'abord, la fonction n'accepte pas un itérateur d'entrée, car ceux-ci ne prennent pas en charge l'avance et la distance.

Deuxièmement, la vérification d'erreur est désactivée. Si je ne me trompe pas, la possibilité de end < tmp signifie qu'un comportement indéfini a été invoqué. Imaginez le conteneur est une liste std ::. Que se passerait-il si vous aviez réussi à avancer beyong list.end()? Mais je pense que ce serait indéfini même avec un vecteur ou un tableau (et MSVC++ serait probablement lancé avec son débogage d'itérateur avant vous).

Ainsi, pour décoder la séquence, je ferais quelque chose comme ceci:

#include <iostream> 
#include <algorithm> 
#include <vector> 
#include <stdexcept> 
#include <iterator> 

template <class InputIterator, class OutputIterator> 
void decode(InputIterator start, InputIterator end, OutputIterator output) 
{ 
    typedef typename std::iterator_traits<InputIterator>::value_type value_type; 
    while (start != end) 
    { 
     value_type count = *start; 
     ++start; 
     value_type result = value_type(); 
     for (value_type i = value_type(); i != count; ++i, ++start) { 
      if (start == end) { 
       throw std::range_error("The input sequence had too few elements."); 
      } 
      result += *start; 
     } 
     *output = result; 
     ++output; 
    } 
} 

int main() 
{ 
    try { 
     std::vector<int> v; 
     decode(std::istream_iterator<int>(std::cin), std::istream_iterator<int>(), std::back_inserter(v)); 
     std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " ")); 
    } 
    catch (const std::exception& e) { 
     std::cout << e.what() << '\n'; 
    } 
} 
+0

réponse Nice. +1 Mais ... fin

+0

Pour certains itérateurs comme 'istream_iterator' qui s'incrémente quand à la fin vous quitte à la fin. D'autres, comme des pointeurs, continuent indéfiniment. Une autre façon de gérer la situation est d'avancer 'run_length-1' et assurez-vous que ce n'est pas la fin, puis faites-en une de plus. –

+0

Cela dépend entièrement de l'implémentation. Avec incrémentation de VC++ au-delà de 'std :: istream_iterator ()' déclenche une erreur avec les itérateurs vérifiés, ainsi que l'avancement d'un itérateur vectoriel au-delà de end(). Aucun algorithme ne devrait jamais avancer au-delà de l'itérateur final. De même, 'it + run_length - 1' n'a pas moins de potentiel en dehors des limites que' it + run_length'. – UncleBens