假设我有两个无符号整数:
size_t A, B;
它们加载了一些随机数,A可能比B更大,相等或更小。我想从A循环到B.但是,比较和增量都取决于哪个更大。
for (size_t i = A; i <= B; ++i) //A <= B
for (size_t i = A; i >= B; --i) //A >= B
明显的暴力解决方案是将这些内容嵌入if语句中:
if (A <= B)
{
for (size_t i = A; i <= B; ++i) ...
}
else
{
for (size_t i = A; i >= B; --i) ...
}
请注意,我必须将从循环到到 B,所以我不能有两个中间整数并将A和B放入正确的插槽中然后进行相同的比较和增量。在“A更大”的情况下,我必须减少,而相反的必须增加。
我可能会有许多嵌套循环需要相同的设置,这意味着每个if / else都会有一个函数调用,我必须传递大量的变量,或者另一个if / else与另一个if / else别的等等。
在不牺牲速度的情况下避免这种情况是否有任何棘手的捷径?功能指针和东西在一个紧凑的,经常重复的循环中听起来非常痛苦。是否有一些疯狂的模板解决方案?
答案 0 :(得分:7)
我的错误,最初误解了这个问题。
要进行从A
到B
的包容性循环,您会遇到棘手的情况。你需要循环一个过去 B.所以你在循环之前计算出这个值。我在for
循环中使用了逗号运算符,但为了清晰起见,您可以将它放在外面。
int direction = (A < B) ? 1 : -1;
for( size_t i = A, iEnd = B+direction; i != iEnd; i += direction ) {
...
}
如果您不介意修改A
和B
,则可以执行此操作(使用A
作为循环变量):
for( B+=direction, A != B; A += direction ) {
}
我有一个游戏......当涉及到函数指针时,不知道内联规则是什么,或者这是否更快,但它无论如何都是一个练习。 =)
inline const size_t up( size_t& val ) { return val++; }
inline const size_t down( size_t& val ) { return val--; }
typedef const size_t (*FnIncDec)( size_t& );
inline FnIncDec up_or_down( size_t A, size_t B )
{
return (A <= B) ? up : down;
}
int main( void )
{
size_t A = 4, B = 1;
FnIncDec next = up_or_down( A, B );
for( next(B); A != B; next(A) ) {
std::cout << A << endl;
}
return 0;
}
回应:
这不适用于案例A = 0,B = UINT_MAX(反之亦然)
这是正确的。问题是由于溢出,i
和iEnd
的初始值变得相同。要处理这个问题,您可以使用do->while
循环。这将删除初始测试,这是多余的,因为您始终至少执行一次循环体...通过删除第一个测试,您迭代过去终止条件第一次。
size_t i = A;
size_t iEnd = B+direction;
do {
// ...
i += direction;
} while( i != iEnd );
答案 1 :(得分:5)
size_t const delta = size_t(A < B? 1 : -1);
size_t i = A;
for( ;; )
{
// blah
if( i == B ) { break; }
i += delta;
}
答案 2 :(得分:2)
你打算怎么处理迭代值?
如果这将成为数组中的某个索引,则应使用相关的iterator
或reverse_iterator
类,并围绕这些类实现算法。您的代码将更强大,更易于维护或发展。此外,标准库中的许多工具都是使用这些接口构建的。
实际上,即使不这样做,也可以实现一个返回自己索引的迭代器类。
您还可以使用一点元编程魔术来定义迭代器的行为方式,按照A
和B
的顺序。
在进一步说明之前,请考虑这只适用于A
和B
的常量值。
template <int A,int B>
struct ordered {
static const bool value = A > B ? false: true;
};
template <bool B>
int pre_incr(int &v){
return ++v;
}
template <>
int pre_incr<false>(int &v){
return --v;
}
template <int A, int B>
class const_int_iterator : public iterator<input_iterator_tag, const int>
{
int p;
public:
typedef const_int_iterator<A,B> self_type;
const_int_iterator() : p(A) {}
const_int_iterator(int s) : p(s) {}
const_int_iterator(const self_type& mit) : p(mit.p) {}
self_type& operator++() {pre_incr< ordered<A,B>::value >(p);return *this;}
self_type operator++(int) {self_type tmp(*this); operator++(); return tmp;}
bool operator==(const self_type& rhs) {return p==rhs.p;}
bool operator!=(const self_type& rhs) {return p!=rhs.p;}
const int& operator*() {return p;}
};
template <int A, int B>
class iterator_factory {
public:
typedef const_int_iterator<A,B> iterator_type;
static iterator_type begin(){
return iterator_type();
}
static iterator_type end(){
return iterator_type(B);
}
};
在上面的代码中,我定义了一个准确的iterator
类,它从A到B的值中进行。有一个简单的元编程测试来确定A和B是否按升序排列,并选择正确的运算符({{ 1}}或++
)来查看值。
最后,我还定义了一个简单的工厂类来保存--
和begin
迭代器方法。使用这个类,你只能为依赖类型值A和B提供一个单一的声明点(I这里的意思是你只需要为这个容器使用A和B一次,并且从那里生成的迭代器将取决于这些相同的A和B,从而稍微简化了代码)。
这里我提供了一个简单的测试程序,输出20到11之间的值。
end
但是,使用标准库可能有更好的方法。
提出了对#define A 20
#define B 10
typedef iterator_factory<A,B> factory;
int main(){
auto it = factory::begin();
for (;it != factory::end();it++)
cout << "iterator is : " << *it << endl;
}
和O
使用UINT_MAX
和A
的问题。我认为应该可以通过使用这些特定值重载模板来处理这些情况(作为读者的练习)。
答案 3 :(得分:0)
size_t A, B;
if (A > B) swap(A,B); // Assuming A <= B, if not, make B to be A
for (size_t i = A; A <= B; ++A) ...