在STL或增强的C ++范围/ xrange等效?

在STL或boost中是否存在用于python Xrange生成器的C ++等价物?

xrange基本上每次调用++运算符都会生成递增的数字。 构造函数是这样的:

xrange(first, last, increment) 

希望能够像这样使用boost来做一些事情:

 foreach(int i, xrange(N)) 

我知道for循环。 在我看来,他们是太多的样板。

谢谢

我的理由是:

我想要这样做的主要原因是因为我使用语音到文本软件,并且编程循环通常的方式很难,即使使用代码完成。 有可读性的结构要高效得多。

许多循环从零开始并递增1,这是范围的默认值。 我发现python构造更直观

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

需要以范围为参数的函数:

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

我理解语言之间的差异,但是如果Python中的特定结构对我非常有用,并且可以用C ++高效地实现,那么我不会看到不使用它的原因。 对于每个构造都是C ++外来的,然而人们使用它。

我把我的实现在页面底部以及示例用法。

在我的领域,我使用multidimensional array,通常排名第四张量。 所以我通常会得到4个不同范围/增量的嵌套循环来计算规范化,索引等,这些不一定是性能循环,而我更关心正确性的可读性和修改能力。

例如

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

在上面,如果需要不同的strids,你必须传递更多的variables,修改循环等,最终你会得到一个整数/几乎相同的循环。

foreach和范围确切地解决我的问题。 熟悉一般C ++程序员对我的关注点不高 – 问题领域是比较模糊的,有很多元编程,SSE内在的,生成的代码。

据我所知,Boost有counting_iterator ,似乎只允许递增1。对于完整的xrangefunction,您可能需要自己实现一个类似的迭代器。

总而言之,它可能看起来像这样(编辑:为xrange的第三个重载添加一个迭代器,以便与boost的迭代器外观一起玩):

 #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 << ' '; } } 

正如其他人所说,我不认为这会给你带来太大的回报。

提升irange应该是真正的答案(ThxPaul Brannan)

我添加了我的答案,以提供 非常 有效的用例的 一个令人信服的例子,这些 用例通过手动循环无法很好地发挥作用:

 #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, ", ")); } 

输出6, 5, 4, 3, 2, 1, 0,

请注意,这与生成器/理解(函数式语言)和枚举(C#)

更新我只是想我会提到C ++ 11允许的以下(非常不灵活)成语:

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

当然你也可以和irange结婚

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

std::iota (尚未标准化)有点像range 。 尽pipe如此,没有比明确for循环变得更短或更清楚。

 #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; } 

g++ -std=c++0x编译g++ -std=c++0x ; 这将打印"1 2 3 4 5 \n"

我想要这样做的主要原因是因为我使用语音到文本软件,并且编程循环通常的方式很难,即使使用代码完成。 有可读性的结构要高效得多。

这就说得通了。 但是不能用简单的macros来解决这个问题吗? #define for_i_to(N, body) for (int i = 0; i < N; ++i) { body }

或类似的东西。 或者完全避免循环,并使用标准库algorithm。 ( std::for_each(range.begin(), rang.end(), myfunctor())似乎更容易发音)

许多循环从零开始并递增1,这是范围的默认值。 我发现python构造更直观

你错了。 Python版本对于Python程序员来说更直观。 非程序员可能更直观。 但是你正在编写C ++代码。 你的目标应该是让C ++程序员直观。 而C ++程序员知道的for循环,他们知道标准库algorithm。 坚持使用这些。 (或者坚持写Python)

需要以范围为参数的函数:

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

还是惯用的C ++版本:

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

在C ++中,范围由迭代器对表示。 不是整数。 如果您要用新语言编写代码,请遵守该语言的惯例。 即使这意味着你必须适应和改变一些习惯。

如果你不愿意这样做,坚持你所知道的语言。

试图把语言X变成语言Y 总是做错事。 它没有工作,它会混淆将维护(或只是读取)你的代码的X语言程序员。

嗯,这是我写的,因为似乎没有一个。 除了单个整数之外,生成器不使用任何内部存储器。 范围对象可以传递并用于嵌套循环。

有一个小的testing用例。

 #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)); } 

由于我已经开始使用BOOST_FOREACH进行所有迭代(可能是一个误导的想法,但这是另一回事),下面是aaa的range类的另一个用法:

 std::vector<int> vec; // ... fill the vector ... BOOST_FOREACH(size_t idx, make_range(0, vec.size())) { // ... do some stuff ... } 

(是的,范围应该模板化,所以我可以使用用户定义的整型types)

这里是make_range():

 template<typename T> range<T> make_range(T const & start, T const & end) { return range<T>(start, end); } 

也可以看看:

http://groups.google.com/group/boost-list/browse_thread/thread/3e11117be9639bd

和:

https://svn.boost.org/trac/boost/ticket/3469

其中提出了类似的解

我刚刚发现boost :: integer_range ; 与上面的例子,代码将如下所示:

 using namespace boost; std::vector<int> vec; // ... fill the vector ... BOOST_FOREACH(size_t idx, make_integer_range(0, vec.size())) { // ... do some stuff ... } 

for循环几乎可以自动处理:

 for(int loop=first;loop < last;loop += increment) { /// Do Stuff. } 

你所做的并不是像你打算的那样有效地工作。 BOOST_FOREACH评估一次参数。 这意味着你需要xrange来产生一个充满你的值的可迭代容器。

也就是说,它可能看起来像这样(注意,可以更通用一些,如采取进步函子等):

 template <typename T> std::vector<T> xrange(const T& pBegin, const T& pEnd) { std::vector<T> v; for (T i = pBegin, i < pEnd; ++i) { v.push_back(i); } return v; } 

但是你所做的只是:

  1. 将for循环移入一个函数,
  2. 浪费时间和资源来分配一个即将死亡的向量

相反,直接使用for循环,就像我前面提到的和其他的提到的一样。

如果你真的反对一个循环(记住你没有编程来保存按键!),你可以使用一个macros和一些魔术(未经testing):

 #define FOR_N(v, s, e) for (BOOST_AUTO(v, s); v < e; ++v) #define for_n FOR_N // better looking for_n(i, 0, N) { // use i; } 

当然现在你必须包含另一个文件,这个代码是:\

你正试图把一个Python成语带入C ++。 这是无稽之谈。 使用

 for(int i=initVal;i<range;i+=increment) { /*loop body*/ } 

为了达成这个。 在Python中, for(i in xrange(init, rng, increment))窗体是必须的,因为Python不提供一个简单的for循环,只有for-eachtypes的构造。 所以你可以迭代一个序列或一个生成器。 这在提供for(;;)语法的语言中是不必要的,几乎可以肯定是不好的做法。

编辑:作为一个完全不build议的旁边,最接近我可以得到的for i xrange(first, last, inc)语法在C + +是:

 #include <cstdio> using namespace std; int xrange(unsigned int last, unsigned int first=0, unsigned int inc=1) { static int i = first; return (i<last)?i+=inc:i=0; } int main() { while(int i=xrange(10, 0, 1)) printf("in loop at i=%d\n",i); } 

不是说,虽然这循环正确的次数,我不同,从first+inclast而不是firstlast-inc如在Python中。 此外,该函数只能工作可靠的unsigned值,因为当i==0while循环将退出。 不要使用这个function。 我只是在这里添加这个代码来certificate这样的事情确实是可能的。 还有其他一些注意事项和细节(例如,代码在第一个函数调用中不会真正起作用!= 0)

既然我们并不真正知道你真的想用什么,我假设你的testing用例是有代表性的。 然后简单的循环是一个更简单,更可读的:

 int main() { for (int i = 0; i <= 6; ++i){ for (int j = 3; j <= 10; j += 3){ std::cout << i << " " << j << "\n"; } } } 

一个C ++程序员可以从街上走进来, 理解这个函数,而不必在其他地方查找复杂的类。 而且是5行而不是60。当然,如果你有400个完全像这样的循环,那么是的,你可以通过使用范围对象来节省一些工作量。 或者你可以把这两个循环包装在一个辅助函数中,并在需要的时候调用

我们没有足够的信息来说明简单的for循环出了什么问题,或者什么是合适的replace。 这里的循环解决了你的问题,复杂程度要低得多,代码的执行也less得多。 如果这是一个糟糕的解决scheme,请告诉我们您的需求(例如,您需要解决什么问题,而不是“我想在C ++中使用Python风格的循环”)

保持简单,制造一个愚蠢的macros观;

 #define for_range(VARNAME, START, STOP, INCREMENT) \ for(int VARNAME = START, int STOP_ = STOP, INCREMENT_ = INCREMENT; VARNAME != STOP_; VARNAME += INCREMENT_) 

并用作;

 for_range(i, 10, 5, -1) cout << i << endl;