Действительно ли там C++ эквивалентен для Python генератор Xrange или в STL или в повышении?
xrange в основном генерирует увеличенное число с каждым вызовом к ++ оператор. конструктор похож на это:
xrange(first, last, increment)
надежда состояла в том, чтобы сделать что-то вроде этого с помощью повышения для каждого:
foreach(int i, xrange(N))
I. знаю для цикла. по-моему, они - слишком много шаблона.
Спасибо
моя главная причина для желания сделать так состоит в том, потому что я использую речь в текстовом программном обеспечении и цикл программирования, обычный путь является трудным, даже если использование завершения кода. Намного более эффективно иметь удобопроизносимые конструкции.
много циклов запускаются с нуля и инкремента одним, который является значением по умолчанию для диапазона. Я нахожу конструкцию 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, ...);
В вышеупомянутом, если различные шаги необходимы, необходимо передать больше переменных, изменить циклы, и т.д. в конечном счете Вы заканчиваете с массой целых чисел/почти идентичные циклы.
foreach и диапазон решают мою проблему точно. знакомство со средним программистом на C++ не высоко в моем списке проблем - проблемная область довольно неясна, существует много метапрограммирования, SSE внутренний, сгенерированный код.
Boost, насколько я знаю, имеет count_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 << ' ';
}
}
Как говорят другие, я не вижу, чтобы это давало вам многого сверх нормы для цикла.
.Цикл обрабатывает почти автоматически:
for(int loop=first;loop < last;loop += increment)
{
/// Do Stuff.
}
std::iota
(еще не стандартизировано) вроде как диапазон
. Однако это не делает вещи короче и яснее, чем явный цикл для
.
#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"
.
Ну, вот что я написал, так как, кажется, его нет. Генератор не использует никакой внутренней памяти, кроме одного целого. Объект диапазона можно передавать и использовать в вложенных циклах.
есть небольшой тестовый случай.
#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));
}
моя основная причина, почему я хочу сделать это, потому что я использую речь в текстовом программном обеспечении, и цикл программирования обычным способом трудно, даже если используется завершение кода. Гораздо эффективнее иметь произносимые конструкции.
В этом есть смысл. Но разве простой макрос не может решить эту проблему? #define for_i_to(N, body) for (int i = 0; i < N; ++i) { body }
или что-то подобное. Или полностью избежать цикла и использовать стандартные алгоритмы библиотеки. (std::for_each(range.begin(), rang.end(), myfunctor())
кажется проще произнести)
многие циклы начинаются с нуля и увеличиваются на единицу, что является значением по умолчанию для диапазона. Я нахожу конструкцию python более интуитивной
Вы ошибаетесь. Версия на Python более интуитивна для программиста с Python. И она может быть более интуитивной для непрограммиста. Но вы пишете код на Си++. Ваша цель должна заключаться в том, чтобы сделать его интуитивным для программиста на Си++. А Си++ программист знает for
-loops и знает стандартные алгоритмы библиотеки. Придерживайтесь их использования. (Или придерживайтесь написания 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, которые будут сопровождать (или просто читать) Ваш код.
Вы пытаетесь привнести идиому питона в С++. Это бессмысленно. Используйте
for(int i=initVal;i<range;i+=increment)
{
/*loop body*/
}
, чтобы достичь этого. На Python форма for(i в xrange(init, rng, increment))
необходима, так как Python не предоставляет простой для цикла, а только конструкцию типа for-each. Поэтому итерацию можно выполнять только через последовательность или генератор. Это просто ненужная и почти наверняка плохая практика в языке, который предоставляет синтаксис for(;;)
.
EDIT: Как совершенно не рекомендуемый вариант, ближе всего к синтаксису 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+inc
до last
и НЕ first
до last-inc
, как в языке Python. Также функция может надежно работать только со значениями unsigned
, так как когда i==0
, цикл while
завершится. Используйте не эту функцию. Я добавил этот код только для того, чтобы показать, что что-то подобное действительно возможно. Есть еще несколько других предостережений и ловушек (код на самом деле не будет работать сначала!=0 на последующих вызовах функций, например)
Поскольку мы не знаем, для чего вы на самом деле хотите это использовать, я предполагаю, что ваше тестовое дело является репрезентативным. И тогда простые для понимания циклы намного проще и читабельнее:
int main() {
for (int i = 0; i <= 6; ++i){
for (int j = 3; j <= 10; j += 3){
std::cout << i << " " << j << "\n";
}
}
}
Программист на Си++ может зайти с улицы и понять эту функцию без необходимости искать сложные классы в другом месте. И это 5 строк вместо ваших 60. Конечно, если у вас 400 циклов именно таких, то да, вы сэкономите некоторое количество усилий, используя свой объект диапазона. Или вы можете просто обернуть эти два цикла внутри вспомогательной функции и вызывать that, когда вам это понадобится.
На самом деле у нас недостаточно информации, чтобы сказать, что не так с простыми для циклов, или что было бы подходящей заменой. Циклы здесь решают вашу проблему с гораздо меньшей сложностью и гораздо меньшим количеством строк кода, чем ваша примерная реализация. Если это плохое решение, расскажите нам о своих требованиях (как в вопросе, какую проблему нужно решить, а не "Я хочу циклы в стиле питона на С++")
.Поскольку я начал использовать BOOST_FOREACH для всех своих итераций (возможно, идея ошибочная, но это уже другая история), вот еще одно использование класса диапазона aaa:
std::vector<int> vec;
// ... fill the vector ...
BOOST_FOREACH(size_t idx, make_range(0, vec.size()))
{
// ... do some stuff ...
}
(да, диапазон должен быть шаблонным, чтобы я мог использовать user -определенные целочисленные типы с ним)
А вот 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 ...
}