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


78

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

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

注意:当然,这与经典的“人口统计”,“横向增加”或“重锤”编程问题非常接近-但并不完全相同。我不需要知道设置了多少个位,只要它不止一个即可。我希望有一种更简单的方法来完成此任务。


(myByte &&(myByte >> 1))有什么帮助?如果myByte == 0x08,则仅设置一位,但表达式的计算结果为true。如果您的意思是(myByte&(myByte >> 1)),则当myByte == 0x0a时,此操作将失败,例如
Michael Burr

布尔值有问题还是?
马丁·约克

1
布尔值或如果至少一个值为True,则返回True。如果多个值是True ,OP希望它返回True。
RobH

@ Michael Burr-抱歉,应该是(myByte&(myByte-1))清除LSB并按位执行AND,以确定是否已设置多个位(如果> 1,则返回false)。对总位数重复此操作,即可获得设置位数的计数。只是进行弹出式计数的一种方法。
奥拉·图维森

位计数是一个有趣的难题:public static int BitCount(int x){return((x == 0)?0:((x <0)?1:0)+ BitCount(x << = 1)); }
Charles Bretana 09年

Answers:


91

怎么样

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

编辑(添加Joel Coehoorn建议:(在.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  ...

要真正做到圆滑,有一个也可以接受动作的覆盖。
Joel Coehoorn

2
建议-一旦b>阈值,就打破循环。
Vilx-

1
sixletter变量,第一个示例出了什么问题。我认为这很简单,可以完成工作。
凯文

@Joel,“条件”是什么意思?如果条件为true,则是要运行的方法的委托?
查尔斯·布雷塔纳

parms-> params。另外,您可以更改该函数以尽早返回。
Mackenir

117

我本来打算编写Linq版本的,但是大约有五个人击败了我。但是我真的很喜欢使用params方法来避免必须手动更新数组。因此,我认为最好的混合动力是基于rp的答案,将车身换成明显的Linqness:

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

清晰易读,易于使用:

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

1
同意,出色的答案。如果有人正在寻找适合一般情况下的解决方案,该@DanielEarwicker答案启发了我这一点:static int MatchCount<T>(Predicate<T> match, params T[] objs) { return objs.Count(obj => match(obj)); }。要计算正值的数量:double a, b, c, d; ... MatchCount(x => x > 0.0, a, b, c, d);等等...
Anders Gustafsson

1
@AndersGustafsson或只是new[] { a, b, c, d }.Count(x => x > 0.0)
Daniel Earwicker 2012年

19

现在该是强制性LINQ答案的时候了,在这种情况下,它实际上是很整洁的。

var bools = new[] { true, true, false, false, false };

return bools.Count(b => b == true) > 1;

7
或者只是Count(b => b)
Daniel Earwicker

16

我只是将它们转换为整数和求和。

除非您处于一个非常紧密的内循环中,否则这样做的好处是易于理解。


2
必须有人添加此版本的linq:myBools.Cast <int>()。Sum()!
珍妮佛

2
@Jennifer对此有点晚了(!),但可悲的是,Cast<int>().Sum()将在一系列布尔值上抛出异常。尽管可以强制转换bool-> int,但是不能强制转换bool-> object-> int,这就是这里的幕后情况。
Daniel Earwicker 2012年

6

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

努力工作以使事情变得清晰,而不是聪明!

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

    return result;
}

5

如果您的意思是大于或等于一个布尔值等于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

5

如果有数百万而不是5的话,您可以避免Count()而改为这样做...

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

建议使用的这种方法的更为简洁的版本:return booleans.Where(b => b).Skip(1).Any()这也适用于我们想知道是否满足条件的N个成员以上的情况。
史蒂夫

5

如果将您的标志打包成一个单词,那么Michael Burr的解决方案将起作用。但是,循环不是必需的:

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

4

比Vilx-s版本更短更丑:

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

1
是的,它太可怕了,但是可以用(我已经验证了所有组合)。
大约

2

从我的头顶上,快速了解此特定示例;您可以将布尔值转换为整数(0或1)。然后遍历Therm并添加它们。如果结果> = 2,则可以执行函数。


2

尽管我喜欢LINQ,但还是有一些类似的问题。

进行计数通常没问题,但是当您计算的项目需要一段时间才能计算/检索时,这可能会成为一个问题。

如果只想检查任何扩展名,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包含AtLeast,AtMost和替代项,使您可以将谓词与AtLeast / Most检查混在一起。[/插头]


1

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

怎么样

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

1
我也是。大声笑。在某些情况下,预计算有助于提高性能-尽管我认为这不是其中之一:-)
frankodwyer

1

我将使用params参数执行类似的操作。

        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语句减少为:return trueCount> = 2
frankodwyer

同样,我不确定这是否特别符合“优雅”的定义
stephenbayer

@frankodwyer没错,我更改了它。我认为使用true和false可能更易读,但是再次查看它肯定会更好。
约翰·桑梅兹

1
if (NumberOfTrue(new List<bool> { bool1, bool2, bool3, bool4 }) >= 2)
{
    // do stuff
}

int NumberOfTrue(IEnumerable<bool> bools)
{
    return bools.Count(b => b);
}

1

不完全漂亮...但这是另一种实现方法:

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

怎么样(a &&(b || c || d || e))|| (b&(c || d || 3))|| (c &&(d || e))|| (d && e)?
stephenbayer

1
很好...我可能会将其用作面试问题...“这是做什么的?” 来衡量纯智能,“您如何看待它作为解决方案?” 淘汰喜欢的人。
ChrisA

哇!谈论暴力和血腥的无知!:-)
RobH

1

我现在好多了,而且很短!

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

2
尽管已经将谓词传递给Count而不需要Where,但已经有几个人写了那个。
丹尼尔·艾威克

1

我想给出一个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 )
{
  ....
}

0

在大多数语言中,true等于一个非零值,而false则为零。我没有确切的语法给您,但是在伪代码中,该怎么办:

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

在true等于任何非零值的任何语言中均无效。(该示例仅在始终使用1表示true的情况下才有效。)
RobH

0

如果只有五个不同的值,则可以通过将这些位打包为short或int并检查其是否为零或一位答案来轻松进行测试。您可能会得到的唯一无效号码是..

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

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

这给您一个简单的答案。

   公共静态布尔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,
            1,0,0,0,0,0,0,0,
            0、0、0、0、0、0、0、0
      };
      if(multiBitLookup [b] == 1)
         返回false;
      返回true;
   }

这不能很好地扩展到8位以上,但是您只有5位。



0

你提到

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

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

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

如果您的意思是“ if (myByte & (myByte >> 1))”,那么(myByte == 0x0a)即使设置了2位,表达式是否仍将返回false。

但是,这里有一些计数单词中位数的技术:

位扭曲的黑客-计数位

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

当然,使用查找表也不错。


-1

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

bool a = true;
bool b = true;
bool c = false;

if (a || b || c)
{
    if (a ^ b ^ c){
        //Throw Error
    }
}

此代码将引发错误,因为a和b都为true。

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

如果有人知道此策略的任何陷阱,我只是在C#中找到了xor运算符,请告诉我。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.