考虑以下示例代码(我实际上使用较长的二进制字符串,但这足以解释问题):
void enumerateAllSubsets(unsigned char d) {
unsigned char n = 0;
do {
cout<<binaryPrint(n)<<",";
} while ( n = (n - d) & d );
}
该函数(由于Knuth)有效地循环遍历二进制字符串的所有子集;
例如:
33 = '00100001' in binary and enumerateAllSubsets(33) would produce:
00000000, 00100000, 00000001, 00100001.
我需要写一个#define来制作
macroEnumerate(n,33)
cout<<binaryPrint(n)<<",";
的行为方式与enumerateAllSubsets(33)相当。 (好吧,订单可能会重新排列)
基本上我需要能够对一组子集执行各种操作。
使用for-loops做类似的事情是微不足道的:
for(int i=0;i < a.size();i++)
foo(a[i]);
可以替换为:
#define foreach(index,container) for(int index=0;index < container.size();index++)
...
foreach(i,a)
foo(a[i]);
enumerateAllSubsets()的问题是循环体需要无条件地执行一次,因此do-while不能被重写为。
我知道问题可以通过STL样式的模板化函数和传递给它的lambda来解决(类似于STL for_each函数),但是一些badass #define宏似乎是一个更清晰的解决方案。
答案 0 :(得分:2)
一种选择是使用始终至少运行一次的for循环,例如:
for (bool once = true; once? (once = false, true) : (n = (n - d) & d); )
// loop body
在第一次迭代中,一次变量被清除,表达式的计算结果为true,因此循环执行。从那时起,实际的测试和步骤逻辑控制循环。
从这里开始,将其重写为宏应该会容易得多。
希望这有帮助!
答案 1 :(得分:2)
假设C ++ 11,定义一个范围对象:
#include <iostream>
#include <iterator>
#include <cstdlib>
template <typename T>
class Subsets {
public:
Subsets(T d, T n = 0) : d_(d), n_(n) { }
Subsets begin() const { return *this; }
Subsets end() const { return {0, 0}; }
bool operator!=(Subsets const & i) const { return d_ != i.d_ || n_ != i.n_; }
Subsets & operator++() {
if (!(n_ = (n_ - d_) & d_)) d_ = 0;
return *this;
}
T operator*() const { return n_; }
private:
T d_, n_;
};
template <typename T>
inline Subsets<T> make_subsets(T t) { return Subsets<T>(t); }
int main(int /*argc*/, char * argv[]) {
int d = atoi(argv[1]);
for (auto i : make_subsets(d))
std::cout << i << "\n";
}
如果你想使用它,我已经非常通用,例如,uint64_t
。
答案 2 :(得分:0)
您可以使用表达式执行多行宏,如下所示:
#define macroenum(n, d, expr ) \
n = 0; \
do { \
(expr); \
} while (n = (n -d) & d) \
; \
int main(int argc, const char* argv[])
{
enumerateAllSubsets(33);
int n;
macroenum(n, 33, cout << n << ",");
}
正如其他人所提到的,许多人认为这不会很干净 - 除其他外,它依赖于范围内存在的变量'n'。您可能需要将expr包装在另一组parens中,但我使用g ++测试它并获得与enumerateAllSubsets相同的输出。
答案 3 :(得分:0)
您的目标似乎是能够执行enumerateAllSubsets
之类的操作,但更改为每次迭代执行的操作。
在C ++中,您可以使用头文件中的函数执行此操作:
template<typename Func>
inline void enumerateAllSubsets(unsigned char d, Func f)
{
unsigned char n = 0;
do { f(n); } while ( n = (n - d) & d );
}
样本用法:
enumerateAllSubsets(33, [](auto n) { cout << binaryPrint(n) << ','; } );