代码之家  ›  专栏  ›  技术社区  ›  Ola Tuvesson

优雅地确定多个布尔值是否为“真”

  •  70
  • Ola Tuvesson  · 技术社区  · 17 年前

    我有一组五个布尔值。如果其中不止一个是真的,我想执行一个特定的函数。您能想到的最优雅的方法是什么,允许我在一个if()语句中检查这个条件?目标语言是C#,但我也对其他语言的解决方案感兴趣(只要我们不谈论特定的内置函数)。

    一个有趣的选择是将布尔值存储在一个字节中,右移并与原始字节进行比较。差不多 if(myByte && (myByte >> 1)) [编辑]对不起,应该是这样的 if(myByte & (myByte - 1))

    注意:这当然非常接近于经典的“总体计数”、“横向加法”或“汉明重量”编程问题,但并不完全相同。我不需要知道设置了多少位,只要它不止一个。我希望有一个更简单的方法来实现这一点。

    22 回复  |  直到 13 年前
        1
  •  120
  •   Daniel Earwicker    17 年前

    我本来打算写Linq版本的,但是有五个人左右抢先完成了。但我非常喜欢params方法,它可以避免手动创建数组。因此,我认为最好的混合动力车是,根据rp的答案,车身替换为明显的灵巧:

    public static int Truth(params bool[] booleans)
    {
        return booleans.Count(b => b);
    }
    

    清晰美观,易于阅读和使用:

    if (Truth(m, n, o, p, q) > 2)
    
        2
  •  94
  •   ThdK    11 年前

      if ((bool1? 1:0) + (bool2? 1:0) + (bool3? 1:0) + 
          (bool4? 1:0) + (bool5? 1:0) > 1)
          // do something
    

    或者一个广义的方法是。。。

       public bool ExceedsThreshold(int threshold, IEnumerable<bool> bools)
        {
           int trueCnt = 0;
           foreach(bool b in bools)
              if (b && (++trueCnt > threshold)) 
                  return true;
           return false;          
        } 
    

    或者按照其他答案的建议使用LINQ:

        public bool ExceedsThreshold(int threshold, IEnumerable<bool> bools)
        { return bools.Count(b => b) > threshold; }
    

    (在.Net 2.x及更高版本中)

        public void ExceedsThreshold<T>(int threshold, 
                          Action<T> action, T parameter, 
                          IEnumerable<bool> bools)
        { if (ExceedsThreshold(threshold, bools)) action(parameter); }
    

    或在.Net 3.5及更高版本中:

        public void ExceedsThreshold(int threshold, 
                Action action, IEnumerable<bool> bools)
        { if (ExceedsThreshold(threshold, bools)) action(); }
    

    或者作为 IEnumerable<bool>

      public static class IEnumerableExtensions
      {
          public static bool ExceedsThreshold<T> 
             (this IEnumerable<bool> bools, int threshold)
          { return bools.Count(b => b) > threshold; }
      }
    

    然后,使用情况将是:

      var bools = new [] {true, true, false, false, false, false, true};
      if (bools.ExceedsThreshold(3))
          // code to execute  ...
    
        3
  •  22
  •   Garry Shutler    17 年前

    var bools = new[] { true, true, false, false, false };
    
    return bools.Count(b => b == true) > 1;
    
        4
  •  16
  •   recursive    17 年前

    我会把它们转换成整数和和。

        5
  •  7
  •   Loki Astari    17 年前

    如果你的意思是大于或等于一个布尔值等于真,你可以这样做

    if (bool1 || bool2 || bool3 || bool4 || bool5)
    

    如果您需要多个(2个及以上)等于true的布尔值,可以尝试

    int counter = 0;
    if (bool1) counter++;
    if (bool2) counter++;
    if (bool3) counter++;
    if (bool4) counter++;
    if (bool5) counter++;
    if (counter >= 2) //More than 1 boolean is true
    
        6
  •  6
  •   rp.    17 年前

    我会写一个函数来接收任意数量的布尔值。它将返回那些为真的值的数目。检查结果,确定要执行某项操作所需的正值数量。

    private int CountTrues( params bool[] booleans )
    {
        int result = 0;
        foreach ( bool b in booleans )
        {
            if ( b ) result++;
        }
    
        return result;
    }
    
        7
  •  6
  •   Community Mohan Dere    9 年前

    如果你的旗帜被包装成一个单词,那么 Michael Burr's solution 会有用的。但是,循环不是必需的:

    int moreThanOneBitSet( unsigned int v)
    {
        return (v & (v - 1)) != 0;
    }
    

    实例

     v (binary) | v - 1 | v&(v-1) | result
    ------------+-------+---------+--------
           0000 |  1111 |    0000 |  false
           0001 |  0000 |    0000 |  false
           0010 |  0001 |    0000 |  false
           0011 |  0010 |    0010 |   true
           .... |  .... |    .... |   ....
           1000 |  0111 |    0000 |  false
           1001 |  1000 |    1000 |   true
           1010 |  1001 |    1000 |   true
           1011 |  1010 |    1010 |   true
           1100 |  1011 |    1000 |   true
           1101 |  1100 |    1100 |   true
           1110 |  1101 |    1100 |   true
           1111 |  1110 |    1110 |   true
    
        8
  •  5
  •   Ian Mercer    16 年前

    如果有数百万而不是只有5个,你可以避免Count(),而是这样做。。。

    public static bool MoreThanOne (IEnumerable<bool> booleans)
    {
        return booleans.SkipWhile(b => !b).Skip(1).Any(b => b);
    }
    
        9
  •  4
  •   some    17 年前

    比Vilx-s版本更短更丑陋:

    if (((a||b||c)&&(d||e))||((a||d)&&(b||c||e))||(b&&c)) {}
    
        10
  •  2
  •   Bill the Lizard    17 年前

    从我的头顶上,快速的接近这个具体的例子;可以将布尔值转换为整数(0或1)。然后通过therm循环并将它们相加。如果结果>=2然后你可以执行你的功能。

        11
  •  2
  •   Cameron MacFarland    17 年前

    一般来说,进行计数是可以的,但当您的计数项目需要一段时间来计算/检索时,可能会成为一个问题。

    如果您只想检查Any,那么Any()扩展方法是很好的,但是如果您想检查Any,那么至少没有内置函数可以执行该操作,而且是懒惰的。

    最后,我编写了一个函数,如果列表中至少有一定数量的项,则返回true。

    public static bool AtLeast<T>(this IEnumerable<T> source, int number)
    {
        if (source == null)
            throw new ArgumentNullException("source");
    
        int count = 0;
        using (IEnumerator<T> data = source.GetEnumerator())
            while (count < number && data.MoveNext())
            {
                count++;
            }
        return count == number;
    }
    

    var query = bools.Where(b => b).AtLeast(2);
    

    这样做的好处是在返回结果之前不需要评估所有项目。

    [插头]我的项目, NExtension

        12
  •  1
  •   frankodwyer    17 年前

    转换为整数和求和应该可以,但这有点难看,在某些语言中可能不可能。

    int count = (bool1? 1:0) + (bool2? 1:0) + (bool3? 1:0) + (bool4? 1:0) + (bool5? 1:0);
    

    或者,如果您不关心空间,您可以预先计算真值表并使用布尔值作为索引:

    if (morethanone[bool1][bool2][bool3][bool4][bool5]) {
     ... do something ...
    }
    
        13
  •  1
  •   John Sonmez    17 年前

            public void YourFunction()
            {
                if(AtLeast2AreTrue(b1, b2, b3, b4, b5))
                {
                    // do stuff
                }
            }
    
            private bool AtLeast2AreTrue(params bool[] values)
            {
                int trueCount = 0;
                for(int index = 0; index < values.Length || trueCount >= 2; index++)
                {
                    if(values[index])
                        trueCount++;
                }
    
                return trueCount > 2;
    
            }
    
        14
  •  1
  •   AndreasN    17 年前
    if (NumberOfTrue(new List<bool> { bool1, bool2, bool3, bool4 }) >= 2)
    {
        // do stuff
    }
    
    int NumberOfTrue(IEnumerable<bool> bools)
    {
        return bools.Count(b => b);
    }
    
        15
  •  1
  •   Vilx-    17 年前

    if (
        (a && (b || c || d || e)) ||
        (b && (c || d || e)) ||
        (c && (d || e)) ||
        (d && e)
    )
    
        16
  •  1
  •   John Sonmez    17 年前

    bool[] bools = { b1, b2, b3, b4, b5 };
    if (bools.Where(x => x).Count() > 1)
    {
       //do stuff
    }
    
        17
  •  1
  •   Scott Aron Bloom    9 年前

    我想给出一个C++11可变模板的答案。

    template< typename T>
    T countBool(T v)
    {
        return v;
    }
    
    template< typename T, typename... Args>
    int countBool(T first, Args... args)
    {
        int boolCount = 0;
        if ( first )
            boolCount++;
        boolCount += countBool( args... );
        return boolCount;
    }
    

    简单地按如下方式调用它,就创建了一种计算布尔数的相当优雅的方法。

    if ( countBool( bool1, bool2, bool3 ) > 1 )
    {
      ....
    }
    
        18
  •  0
  •   Bork Blatt    17 年前

    if ((bool1 * 1) + (bool2 * 1) + (bool3 * 1) > 2)
    {
        //statements here
    }
    
        19
  •  0
  •   Jens Wessling Jens Wessling    17 年前

    如果您只有五个不同的值,那么您可以通过将位打包成一个short或int并检查它是否是零位或一位答案中的任何一个来轻松地进行测试。你能得到的唯一无效数字是。。

    0x 0000 0000 
    0x 0000 0001
    0x 0000 0010
    0x 0000 0100
    0x 0000 1000
    0x 0001 0000
    

    这将为您提供六个要搜索的值,将它们放在查找表中,如果不在其中,您就有了答案。

       public static boolean moreThan1BitSet(int b)
       {
          final short multiBitLookup[] = { 
                1, 1, 1, 0, 1, 0, 0, 0,
                1, 0, 0, 0, 0, 0, 0, 0,
                0, 0, 0, 0, 0, 0, 0, 0,
                1, 0, 0, 0, 0, 0, 0, 0,
                0, 0, 0, 0, 0, 0, 0, 0
          };
          if(multiBitLookup[b] == 1)
             return false;
          return true;
       }
    

        20
  •  0
  •   Partha Choudhury    17 年前

    如果((b1.比较到(假)+b2.比较到(假)+b3.比较到(假)+……)>1)

    ...

    其他的

    ...

        21
  •  0
  •   Michael Burr    17 年前

    进行右移并与原始字节进行比较。 差不多 if (myByte && (myByte >> 1))

    我认为该表达式不会给出您想要的结果(至少使用C语义,因为该表达式不是有效的C#):

    (myByte == 0x08) ,则表达式将返回true,即使只设置了一个位。

    如果你是说“ if (myByte & (myByte >> 1)) (myByte == 0x0a) 即使设置了2位,表达式也将返回false。

    Bit Twiddling Hacks - Counting bits

    你可能会考虑使用Kernighan的计数方法,但是只要你只需要知道有一个以上的比特集合,就可以提前跳出:

    int moreThanOneBitSet( unsigned int v)
    {
        unsigned int c; // c accumulates the total bits set in v
    
        for (c = 0; v && (c <= 1); c++)
        {
          v &= v - 1; // clear the least significant bit set
        }
    
        return (c > 1);
    }
    

    当然,使用查找表也是一个不错的选择。

        22
  •  -1
  •   dmoore1181    12 年前

    我最近也遇到了同样的问题,我有三个布尔值,我需要检查一次只有一个是真的。为此,我使用了xor运算符,如下所示:

    bool a = true;
    bool b = true;
    bool c = false;
    
    if (a || b || c)
    {
        if (a ^ b ^ c){
            //Throw Error
        }
    }
    

    供参考: http://www.dotnetperls.com/xor