在STL或boost中是否有C ++等效的python Xrange生成器?
xrange基本上生成递增的数字,每次调用++运算符。 构造函数是这样的:
xrange(first, last, increment)
希望为每个人使用提升做这样的事情:
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 ++都是外来的,但是人们使用它。
我将我的实现放在页面底部以及示例用法。
在我的域中我使用多维数组,通常排名为4张。所以我经常会得到4个具有不同范围/增量的嵌套循环来计算规范化,索引等等。这些不一定是性能循环,而且我更关心正确性的可读性和修改能力。
例如
int function(int ifirst, int ilast, int jfirst, int jlast, ...);
versus
int function(range irange, range jrange, ...);
在上面,如果需要不同的strid,你必须传递更多的变量,修改循环等等。最终你会得到大量的整数/几乎相同的循环。
foreach和range完全解决了我的问题。对普通C ++程序员的熟悉程度并不高我的关注列表 - 问题域是一个相当模糊,有很多元编程,SSE内在,生成代码。
答案 0 :(得分:51)
提升 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;
答案 1 :(得分:20)
Boost有counting_iterator,这似乎只允许以1为增量递增。对于完整的xrange功能,你可能需要自己实现一个类似的迭代器。
总而言之,它可能看起来像这样(编辑:为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 << ' ';
}
}
正如其他人所说的那样,我并不认为这比正常的循环更能吸引你。
答案 2 :(得分:3)
std::iota
(尚未标准化)有点像range
。但是,与明确的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
汇编;这会打印"1 2 3 4 5 \n"
。
答案 3 :(得分:2)
有一个小测试案例。
#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));
}
答案 4 :(得分:1)
我想要这样做的主要原因是因为我使用语音文本软件,并且编程循环通常很难,即使使用代码完成。拥有可发音的结构会更有效率。
这是有道理的。但是一个简单的宏不能解决这个问题吗? #define for_i_to(N, body) for (int i = 0; i < N; ++i) { body }
或类似的东西。或者完全避免循环并使用标准库算法。 (std::for_each(range.begin(), rang.end(), myfunctor())
似乎更容易发音)
许多循环从零开始并递增1,这是范围的默认值。我发现python构造更直观
你错了。 Python版本对Python程序员来说更直观。并且可能对非程序员更直观。但是你正在编写C ++代码。您的目标应该是让C ++程序员直观。 C ++程序员知道for
- 循环,他们知道标准库算法。坚持使用那些。 (或坚持写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程序员。
答案 5 :(得分:1)
因为我已经开始使用BOOST_FOREACH进行所有迭代(可能是误导的想法,但这是另一个故事),这是aaa范围类的另一个用途:
std::vector<int> vec;
// ... fill the vector ...
BOOST_FOREACH(size_t idx, make_range(0, vec.size()))
{
// ... do some stuff ...
}
(是的,范围应该模板化,以便我可以使用用户定义的整数类型)
这是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 ...
}
答案 6 :(得分:0)
for循环几乎自动处理:
for(int loop=first;loop < last;loop += increment)
{
/// Do Stuff.
}
答案 7 :(得分:0)
由于我们实际上并不知道您真正想要使用它,我假设您的测试用例具有代表性。然后简单的for循环就简单易懂了:
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循环有什么问题,或者什么是合适的替代品。这里的循环解决了您的问题,其复杂性远远低于您的示例实现,而且代码行数要少得多。如果这是一个糟糕的解决方案,请告诉我们您的要求(如您需要解决的问题,而不是“我想在C ++中使用python风格的循环”)
答案 8 :(得分:0)
C ++ 20的Ranges头具有iota_view
,可做到这一点:
#include <ranges>
#include <vector>
#include <iostream>
int main()
{
for (int i : std::views::iota{1, 10})
std::cout << i << ' ';
std::cout << '\n';
for (int i : std::views::iota(1) | std::views::take(9))
std::cout << i << ' ';
}
输出:
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
答案 9 :(得分:-1)
你正试图将一个python习惯用法带入C ++。这是不必要的。使用
for(int i=initVal;i<range;i+=increment)
{
/*loop body*/
}
实现这一目标。在Python中,for(i in xrange(init, rng, increment))
形式是必需的,因为Python不提供简单的for循环,只提供for-each类型的构造。因此,您只能在序列或生成器上进行迭代。在提供for(;;)
语法的语言中,这几乎是不必要的,几乎肯定是不好的做法。
编辑:作为一个完全不推荐的旁白,我在C ++中最接近for i xrange(first, last, inc)
语法的是:
#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+inc
到last
之间的变化不是first
到last-inc
。此外,该函数只能与unsigned
值一起可靠地工作,因为当i==0
时,while
循环将退出。 不使用此功能。我在这里只添加了这段代码,以证明这种类型确实是可行的。还有其他一些注意事项和陷阱(例如,代码对于后续函数调用首先不会起作用!= 0)
答案 10 :(得分:-1)
保持简单,制作一个愚蠢的宏;
#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;