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

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

一个有趣的select是将布尔值存储在一个字节中,进行右移,并与原始字节进行比较。 就像if(myByte && (myByte >> 1))但是这需要转换单独的布尔值为一个字节(通过bitArray?),似乎有点(双关语意)笨拙… 对不起,应该已经 if(myByte & (myByte - 1)) [/编辑]

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

怎么样

  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; } 

或者使用其他答案build议的LINQ:

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

编辑(加上Joel Coehoorn的build议:(在.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 ... 

我要写Linq的版本,但有五个人打我。 但我真的很喜欢params的方法来避免手动新build一个数组。 所以我认为最好的混合体是基于rp的身体代替明显的Linqness的答案:

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

美丽清晰的阅读,并使用:

 if (Truth(m, n, o, p, q) > 2) 

现在是强制LINQ答案的时候了,在这种情况下,这个答案实际上是非常整齐的。

 var bools = new[] { true, true, false, false, false }; return bools.Count(b => b == true) > 1; 

我只是把他们投入和总结。

除非你处在一个超级紧张的内部循环中,否则就会容易理解。

我会写一个函数来接收任意数量的布尔值。 它会返回那些值为真的数字。 检查结果中你需要积极的值的数量来做一些事情。

更努力地说清楚,不是聪明!

 private int CountTrues( params bool[] booleans ) { int result = 0; foreach ( bool b in booleans ) { if ( b ) result++; } return result; } 

如果有数百万,而不是只有5,你可以避免计数(),并做这个,而不是…

 public static bool MoreThanOne (IEnumerable<bool> booleans) { return booleans.SkipWhile(b => !b).Skip(1).Any(b => b); } 

如果你的旗帜被压缩成一个单词,那么Michael Burr的解决scheme将会起作用。 但是,循环不是必需的:

 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 

比Vilx-s更短,更丑:

 if (((a||b||c)&&(d||e))||((a||d)&&(b||c||e))||(b&&c)) {} 

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

 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 

从我的头顶来看,这个具体的例子是一个快速的方法; 你可以将bool转换为一个int(0或1)。 然后通过热循环并添加它们。 如果结果> = 2,那么你可以执行你的function。

虽然我喜欢LINQ,但也有一些漏洞,就像这个问题一样。

一般来说做点数是没有问题的,但是如果你的点数需要一些时间来计算/检索的话,这可能会成为一个问题。

Any()扩展方法是好的,如果你只是想检查任何,但如果你想检查,至less有没有内置的function,将做到这一点,懒惰。

最后,我写了一个函数,如果列表中至less有一定数量的项目,则返回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包含AtLeast,AtMost和覆盖,允许你混合使用AtLeast / Most检查谓词。 [/插头]

拼写和求和应该可以工作,但是有点难看,在某些语言中可能是不可能的。

感觉如何?

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

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

 if (morethanone[bool1][bool2][bool3][bool4][bool5]) { ... do something ... } 

我会用这个参数来做这样的事情。

  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; } 
 if (NumberOfTrue(new List<bool> { bool1, bool2, bool3, bool4 }) >= 2) { // do stuff } int NumberOfTrue(IEnumerable<bool> bools) { return bools.Count(b => b); } 

不完全漂亮…但是,这是另一种方式来做到这一点:

 if ( (a && (b || c || d || e)) || (b && (c || d || e)) || (c && (d || e)) || (d && e) ) 

我现在有一个更好的,非常短的!

 bool[] bools = { b1, b2, b3, b4, b5 }; if (bools.Where(x => x).Count() > 1) { //do stuff } 

在大多数语言中,true等于非零值,而false则为零。 我没有给你准确的语法,但是在伪代码中,那么:

 if ((bool1 * 1) + (bool2 * 1) + (bool3 * 1) > 2) { //statements here } 

如果你只有五个不同的值,你可以很容易地进行testing,把数据打包成一个短或一个整数,并检查是否是零或一位答案。 唯一无效的数字,你可以得到的是..

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

这给你六个值来search,把它们放在一个查找表中,如果它不在那里,你有你的答案。

这给你一个简单的答案。

    public static boolean moreThan1BitSet(int b)
    {
      最后简短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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
             0,0,0,0,0,0,0,0
       };
      如果(multiBitLookup [b] == 1)
         返回false;
      返回true;
    }

这不能很好地扩展到8位,但你只有五位。

if((b1.CompareTo(false)+ b2.CompareTo(false)+ b3.CompareTo(false)+ …)> 1)

//不止一个是真的

其他

你提到

一个有趣的select是将布尔值存储在一个字节中,进行右移,并与原始字节进行比较。 像if (myByte && (myByte >> 1))

我不认为expression式会给你你想要的结果(至less使用C语义,因为expression式是无效的C#):

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

如果你的意思是“ if (myByte & (myByte >> 1)) ”,那么if (myByte == 0x0a)expression式将返回假,即使有2位设置。

但是这里有一些技术来计算一个字的位数:

Bit Twiddling Hacks – 计数位

你可能会考虑的一种变化是使用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); } 

当然,使用查找表也不是一个坏的select。

我想给一个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; } 

简单地调用它如下创build一个相当优雅的方法来计算布尔的数量。

 if ( countBool( bool1, bool2, bool3 ) > 1 ) { .... } 

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

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

这段代码会抛出一个错误,因为a和b都是真的。

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

我只是在C#中find了xor操作符,如果有人知道这个策略的任何陷阱,请告诉我。