37 votes

Équivalent gamme C ++ / gamme xrange en LIST ou en boost?

Est-il C++ équivalent en python Xrange générateur dans la STL ou coup de pouce?

xrange génère essentiellement nombre incrémenté à chaque appel à ++ opérateur. le constructeur est comme ceci:

xrange(first, last, increment)

espérais faire quelque chose comme ceci à l'aide de boost pour chaque:

foreach(int i, xrange(N))

I. suis conscient de la boucle for. à mon avis, ils sont trop passe-partout.

Merci

mes raisons:

ma principale raison de vouloir le faire c'est parce que je utiliser la parole en texte logiciel et de la programmation, de la boucle de façon habituelle est difficile, même si l'aide de la complétion de code. Il est beaucoup plus efficace d'avoir prononçable constructions.

de nombreuses boucles commencent à zéro et d'incrément par un, ce qui est par défaut de gamme. - Je trouver python construire plus intuitive

 for(int i = 0; i < N; ++i)
 foreach(int i, range(N))

les fonctions qui ont besoin de prendre de gamme comme argument:

 Function(int start, int and, int inc);
 function(xrange r);

Je comprends les différences entre les langues, toutefois si l'une de construire en python est très utile pour moi et peut être mis en œuvre de manière efficace en C++, je ne vois pas une raison de ne pas l'utiliser. Pour chaque construction est étrangère à la C++ cependant les gens à l'utiliser.

J'ai mis ma mise en œuvre au bas de la page ainsi l'exemple d'utilisation.

dans mon domaine, je travaille avec des tableaux multidimensionnels, souvent tenseur de rang 4. je voudrais donc souvent jusqu'à la fin avec 4 boucles imbriquées avec les différentes gammes/incréments de calculer la normalisation, index, etc. ceux qui ne sont pas nécessairement les performances des boucles, et je suis plus préoccupé par la justesse de la lisibilité et de la capacité à modifier.

par exemple

int function(int ifirst, int ilast, int jfirst, int jlast, ...);
versus
int function(range irange, range jrange, ...);

Ci-dessus, si différents strids sont nécessaires, vous devez passer plus de variables, de modifier les boucles, etc. finalement, vous vous retrouvez avec une masse d'entiers/près de boucles identiques.

foreach et plage de résoudre mon problème exactement. la familiarité à la moyenne programmeur C++ n'est pas sur ma liste de préoccupations - domaine du problème est assez obscure, il y a beaucoup de méta-programmation, l'ESS intrinsèque, le code généré.

53voto

sehe Points 123151

Boost irange devrait vraiment être la réponse (ThxPaul Brannan)

Je vais ajouter ma réponse à fournir un exemple éloquent de la très valable des cas d'utilisation qui ne sont pas bien servis par le bouclage manuel:

#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/irange.hpp>

using namespace boost::adaptors;

static int mod7(int v) 
    { return v % 7; }

int main() 
{
    std::vector<int> v;

    boost::copy(
            boost::irange(1,100) | transformed(mod7), 
            std::back_inserter(v));

    boost::sort(v);

    boost::copy(
            v | reversed | uniqued, 
            std::ostream_iterator<int>(std::cout, ", "));
}

Sortie: 6, 5, 4, 3, 2, 1, 0,

Notez comment cela ressemble à des générateurs/interprétations (les langages fonctionnels) et enumerables (C#)

Mise à jour j'ai juste pensé que je voudrais vous parler de la suite (très rigide) langage C++11 permet de:

for (int x : {1,2,3,4,5,6,7})
    std::cout << x << std::endl;

bien entendu, on pourrait se marier avec irange:

for (int x : boost::irange(1,8))
    std::cout << x << std::endl;

21voto

UncleBens Points 24580

Boost a counting_iterator autant que je sache, ce qui semble autoriser uniquement l'incrémentation en étapes de 1. Pour plein xrange fonctionnalités dont vous pourriez avoir besoin pour mettre en œuvre une semblable itérateur vous-même.

Dans l'ensemble, il pourrait ressembler à ceci (edit: ajout d'un itérateur pour la troisième surcharge de xrange, jouer avec les boost de l'itérateur de la façade):

#include <iostream>
#include <boost/iterator/counting_iterator.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/foreach.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <cassert>

template <class T>
boost::iterator_range<boost::counting_iterator<T> > xrange(T to)
{
    //these assertions are somewhat problematic:
    //might produce warnings, if T is unsigned
    assert(T() <= to);
    return boost::make_iterator_range(boost::counting_iterator<T>(0), boost::counting_iterator<T>(to));
}

template <class T>
boost::iterator_range<boost::counting_iterator<T> > xrange(T from, T to)
{
    assert(from <= to);
    return boost::make_iterator_range(boost::counting_iterator<T>(from), boost::counting_iterator<T>(to));
}

//iterator that can do increments in steps (positive and negative)
template <class T>
class xrange_iterator:
    public boost::iterator_facade<xrange_iterator<T>, const T, std::forward_iterator_tag>
{
    T value, incr;
public:
    xrange_iterator(T value, T incr = T()): value(value), incr(incr) {}
private:
    friend class boost::iterator_core_access;
    void increment() { value += incr; }
    bool equal(const xrange_iterator& other) const
    {
        //this is probably somewhat problematic, assuming that the "end iterator"
        //is always the right-hand value?
        return (incr >= 0 && value >= other.value) || (incr < 0 && value <= other.value);
    }
    const T& dereference() const { return value; }
};

template <class T>
boost::iterator_range<xrange_iterator<T> > xrange(T from, T to, T increment)
{
    assert((increment >= T() && from <= to) || (increment < T() && from >= to));
    return boost::make_iterator_range(xrange_iterator<T>(from, increment), xrange_iterator<T>(to));
}

int main()
{
    BOOST_FOREACH(int i, xrange(10)) {
        std::cout << i << ' ';
    }
    BOOST_FOREACH(int i, xrange(10, 20)) {
        std::cout << i << ' ';
    }
    std::cout << '\n';
    BOOST_FOREACH(int i, xrange(0, 46, 5)) {
        std::cout << i << ' ';
    }
    BOOST_FOREACH(int i, xrange(10, 0, -1)) {
        std::cout << i << ' ';
    }
}

Comme les autres disent, je ne vois pas cet achat vous beaucoup plus normale de la boucle.

3voto

ephemient Points 87003

std::iota (pas encore normalisé) est un peu comme range . Ne rend cependant pas les choses plus courtes ou plus claires qu'une boucle explicite for , cependant.

 #include <algorithm>
#include <iostream>
#include <iterator>
#include <numeric>
#include <vector>
int main() {
    std::vector<int> nums(5);
    std::iota(nums.begin(), nums.end(), 1);
    std::copy(nums.begin(), nums.end(),
            std::ostream_iterator<int>(std::cout, " "));
    std::cout << std::endl;
    return 0;
}
 

Compiler avec g++ -std=c++0x ; cela imprime "1 2 3 4 5 \n" .

2voto

Anycorn Points 20521

Eh bien, voici ce que j'ai écrit, car il ne semble pas en être un. le générateur n'utilise pas de mémoire interne en plus du nombre entier unique. L'objet range peut être passé et utilisé dans des boucles imbriquées.

il y a un petit cas de test.

 #include "iostream"
#include "foreach.hpp"

#include "boost/iterator/iterator_categories.hpp"

struct range {

  struct iterator_type {
    typedef int value_type;
    typedef int difference_type;
    typedef boost::single_pass_traversal_tag iterator_category;
    typedef const value_type* pointer;
    typedef const value_type & reference;

    mutable value_type value;
    const difference_type increment;

    iterator_type(value_type value, difference_type increment = 0)
      : value(value), increment(increment) {}

    bool operator==(const iterator_type &rhs) const {
      return value >= rhs.value;
    }
    value_type operator++() const { return value += increment; }
    operator pointer() const { return &value; }
  };

  typedef iterator_type iterator;
  typedef const iterator_type const_iterator;

  int first_, last_, increment_;

  range(int last) : first_(0), last_(last), increment_(1) {}
  range(int first, int last, int increment = 1)
    : first_(first), last_(last), increment_(increment) {}

  iterator begin() const {return iterator(first_, increment_);}
  iterator end() const {return iterator(last_);}
};

int test(const range & range0, const range & range1){
  foreach(int i, range0) {
    foreach(int j, range1) {
      std::cout << i << " " << j << "\n";
    }
  }
}

int main() {
  test(range(6), range(3, 10, 3));
}
 

1voto

jalf Points 142628

ma principale raison de vouloir le faire c'est parce que je utiliser la parole en texte logiciel et de la programmation, de la boucle de façon habituelle est difficile, même si l'aide de la complétion de code. Il est beaucoup plus efficace d'avoir prononçable constructions.

Qui fait sens. Mais ne pourrait-il pas une macro simple de résoudre ce problème? #define for_i_to(N, body) for (int i = 0; i < N; ++i) { body }

ou quelque chose de similaire. Ou d'éviter la boucle et utiliser les algorithmes de la bibliothèque standard. (std::for_each(range.begin(), rang.end(), myfunctor()) semble plus facile à prononcer)

de nombreuses boucles commencent à zéro et d'incrément par un, ce qui est par défaut de gamme. - Je trouver python construire plus intuitive

Vous avez tort. La version de Python est plus intuitif pour un programmeur Python. Et il peut être plus intuitif pour un non-programmeur. Mais vous écrivez du code C++. Votre objectif devrait être de rendre intuitive pour un programmeur C++. Et programmeur C++ savez for-boucles et ils savent que les algorithmes de la bibliothèque standard. Tenir à l'utilisation de ceux-ci. (Ou s'en tenir à l'écriture de Python)

les fonctions qui ont besoin de prendre de gamme comme argument:

Function(int start, int and, int inc);
function(xrange r);

Ou la idiomatiques C++ version:

template <typename iter_type>
void function(iter_type first, iter_type last);

En C++, les gammes sont représentées par itérateur paires. Pas des entiers. Si vous allez écrire du code dans une nouvelle langue, respecter les conventions de la langue. Même si cela signifie que vous avez à s'adapter et à changer certaines habitudes.

Si vous n'êtes pas prêt à le faire, coller avec la langue que vous connaissez.

En essayant de tourner la langue X dans la langue Y est toujours la mauvaise chose à faire. Il est propre pas de travail, et il va confondre le langage X programmeurs qui vont maintenir (ou tout simplement lire) votre code.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X