带标志的数组

时间:2011-06-07 09:28:18

标签: c++ algorithm

假设我有一个bool标志数组,它将设置为true或false取决于条件。

假设索引1,2,6已经设置且所有其他标志都没有设置,我需要调用functionA,如果索引2,3,5已经设置,所有其他标志都没有设置,我需要拨打functionB。除了这样做之外,还有一种简单的方法可以执行上述逻辑:

if(array[1] == true && array[2] == true && array[6] == true && 
   array[3] == false && array[4] == false && array[5] == false)
{
  functionA();
}

8 个答案:

答案 0 :(得分:8)

维护和可读性噩梦!

请考虑一下:

bool temperature_sensor_tripped(const bool_array& flags)
{
     return flags[1];
}

// [...]

if (temperature_sensor_tripped(array) 
    && moisture_sensor_tripped(array)
    && !alarm_dispatched(array))
{
    functionA();
}

这样做的好处是moisture_sensor_tripped()及其亲属可以在没有你(或维护者)记住标志顺序的情况下从其他函数调用。

答案 1 :(得分:5)

建议:

bool truecondition = array[1] && array[2] && array[6];
bool falsecondition = !array[3] && !array[4] && !array[5];

if (truecondition && falsecondition)
{
//do something
}

答案 2 :(得分:3)

您可以跳过不必要的== true比较,并对!案例使用false

if (array[1] && array[2] && array[6] && !array[3] && !array[4] && !array[5])

或者,制作一个bool数组并与之进行比较:

bool condition[6] = { true, true, false, false, false, true };
if (std::equal(array+1, array+7, condition)) { // +1 for your 1-based indexes

答案 3 :(得分:3)

而不是bool,您可以使用char代替,并将每个位视为一个标志。假设这个 char 标志(实际上是许多标志的组合)是这样的:

char flags;

因此,如果设置索引1,2,6,则意味着

//index count starts from rightmost bit
flags = 0100 0110 (binary) =  64 + 4 + 2 = 70 (decimal)

同样,索引2,3,5设置意味着

flags = 0010 1100 (binary) =  32 + 8 + 4 = 44 (decimal)

所以你可以写

if (flags == 70 ) //i.e when index 1,2,6 are set
{
     functionA();
}
else if ( flags == 44 ) //i.e when index 2,3,5 are set
{
    functionB();
}

由于char的大小是一个字节,因此您的计算机上最多可以包含8个标记(假设CHAR_BIT8,这很可能是是真实的)。但是你需要更多的标志,然后你可以改为int,并相应地工作。


嗯,这是基本的想法。现在您可以按如下方式改进:

enum flag
{
     flag0 = 1 << 0, //1   = 0000 0001
     flag1 = 1 << 1, //2   = 0000 0010
     flag2 = 1 << 2, //4   = 0000 0100
     flag3 = 1 << 3, //8   = 0000 1000
     flag4 = 1 << 4, //16  = 0001 0000
     flag5 = 1 << 5, //32  = 0010 0000
     flag6 = 1 << 6, //64  = 0100 0000
     flag7 = 1 << 7, //128 = 1000 0000
};

char flags = 0 ;

//this is how you can set flags!
flags |= flag1 ;         //set index 1        (one at a time)
flags |= flag2 | flag6 ; //set index 2 and 6  (more than one at a time)

if ( flags == (flag1 | flag2 | flag6) )
{
      functionA();
} 
else if ( flags == (flag2 | flag3 | flag5) )
{
      functionB();
} 

//and this is how you can unset flags!
flags &= ~flag1 ;          //unset index 1        (one at a time)
flags &= ~flag2 & ~flag6 ; //unset index 2 and 6  (more than one at a time)

请参阅此在线演示:http://www.ideone.com/6BdGf

答案 4 :(得分:2)

如果数组具有固定大小并使用简单掩码进行测试,则可以使用std::bitset

#include <bitset>

void funcA(){
}

void funcB(){
}

enum FuncMasks{
  funcA_Mask = 0x23, // 0010 0011
  funcB_Mask = 0x16, // 0001 0110
};

int main(){
  std::bitset<6> flags;

  if(flags.to_ulong() & funcA_Mask)
    funcA();
  else if(flags.to_ulong() & funcB_Mask)
    funcB();
}

答案 5 :(得分:1)

更简单的方法是对所有这些标志使用整数,然后使用位操作。如果您正在寻找可以使用的解决方案,也可以使用std::bitset

enum flags { // i use hexadecimal notation as it's easier to see unused flags - using 8 bits/1 byte is enough for you but you could extend it even more as well
    FLAG_NONE  = 0x00;
    FLAG_ONE   = 0x01;
    FLAG_TWO   = 0x02;
    FLAG_THREE = 0x04;
    // ...
};

// To set some flags you use bitwise operators:
char flags_set = FLAG_ONE | FLAG_THREE | FLAG_SIX;
flags_set |= FLAG_FOUR; // add fourth flag
flags_set &= ~FLAG_TWO; // remove second flag (if set)

// Similar way you can check the flags and you've got "talking" code that's a lot easier to understand than tons of if()s
if (flags_set & (FLAG_ONE | FLAG_TWO | FLAG_SIX))
    functionA();
elseif (flags_set & (FLAG_TWO | FLAG_THREE | FLAG_FIVE))
    functionB();

答案 6 :(得分:0)

以下是使用valarray的替代方法:

#include <valarray>
#include <iostream>

bool all(const std::valarray<bool> & va) {
  return va.min();
}

bool none(const std::valarray<bool> & va) {
  return !va.max();
}

int main() {
  bool bits_init[] = {0, 1, 1, 0, 0, 0, 1};
  std::valarray<bool> bits(bits_init, 7);
  size_t true_bits_init[] = {1, 2, 6};
  std::valarray<size_t> true_bits(true_bits_init, 3);
  size_t false_bits_init[] = {0, 3, 4, 5};
  std::valarray<size_t> false_bits(false_bits_init, 4);

  if(all(bits[true_bits]) && none(bits[false_bits]))
    std::cout << "functionA" << std::endl;
  else
    std::cout << "functionB" << std::endl;
  bits[2] = false;
  if(all(bits[true_bits]) && none(bits[false_bits]))
    std::cout << "functionA" << std::endl;
  else
    std::cout << "functionB" << std::endl;
  bits[2] = true;
  bits[3] = true;
  if(all(bits[true_bits]) && none(bits[false_bits]))
    std::cout << "functionA" << std::endl;
  else
    std::cout << "functionB" << std::endl;
}

我不知道你是否需要它。如果阵列的大小不同,它确实提供了很大的灵活性。

答案 7 :(得分:0)

如果函数有一个可以在布尔上下文中计算的返回类型(例如bool,int,double,pointer),你可以写:

Array& a = array; // for brevity...
a[1] && a[2] && a[6] && functionA() ||
a[2] && a[3] && a[5] && functionB() ||
...

您可能会或可能不会将此&&函数链接到明确的“if”用法(上面的风格受到一些perl黑客,FWIW的欢迎)。参考的其他方面,这是真正重复的代码的好主意。

如果函数缺少这样的返回类型,则可以使用带有返回类型的包装器来调用它们。

或者,您可以使用operator[]提供索引访问权限,而不是使用普通数组,然后向其添加功能,允许:

array.run_if(1, 2, 6, functionA)
     .run_if(2, 3, 5, functionB);

这里,主要问题是如何处理不同数量的指数。选项包括:

    假设上限是可管理的,
  • 1,2,3,4等索引的重载
  • 使用预处理器工具自动生成一组大而有限的重载(参见Loki或boost示例)
  • 首先使用该函数,然后使用...stdargs,使用-1(没有任意限制,但更容易出错)的标记值
  • 接受std::vector<>数组中的索引(模板可以绑定,发现元素数量,但C ++缺少“数组文字”,所以你需要一个命名变量而不是{{1} }。

如图所示,run_if([1, 2, 6], functionA)调用的链接建议run_if返回对run_if的引用,但这意味着在第一次匹配后继续匹配和潜在的函数调用。某些对象状态需要跟踪并阻止这种情况。