web-dev-qa-db-ja.com

複数のブール値が「true」であるかどうかをエレガントに判断する

5つのブール値のセットがあります。これらの複数が当てはまる場合、特定の機能を実行したいです。あなたが考えることができる最もエレガントな方法は何ですか?単一のif()ステートメントでこの条件をチェックできますか?ターゲット言語はC#ですが、他の言語のソリューションにも興味があります(特定の組み込み関数について話していない限り)。

興味深いオプションの1つは、ブール値をバイトに格納し、右シフトを行って元のバイトと比較することです。 if(myByte && (myByte >> 1))のようなものですが、これには個別のブール値を(bitArrayを介して)バイトに変換する必要があり、それは少し(しゃれた意図的な)不器用なようです... [編集]申し訳ありませんが、されているif(myByte & (myByte - 1))[/ edit]

注:もちろん、これは古典的な「人口数」、「横方向の加算」、または「ハミング重量」プログラミング問題に非常に近いですが、まったく同じではありません。複数のビットが設定されている場合にのみ、ビットがいくつ設定されているかを知る必要はありません。私の希望は、これを達成するはるかに簡単な方法があることです。

73
Ola Tuvesson

どう?

  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  ...
85
Charles Bretana

私はLinqバージョンを書くつもりでしたが、5人かそこらの人がそれに私をbeatりました。しかし、配列を手動で新しくする必要がないようにするためのparamsアプローチが本当に好きです。だから私は最高のハイブリッドは、ボディが明白なLinqnessに置き換えられたrpの答えに基づいていると思う:

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

読みやすく、使用するための美しく明確な:

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

必須のLINQ回答の時間です。この場合、実際には非常に適切です。

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

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

私はそれらをintとsumにキャストするだけです。

非常にタイトな内部ループにいる場合を除き、それは理解しやすいという利点があります。

16
recursive

任意の数のブール値を受け取る関数を作成します。真の値の数を返します。結果を確認して、何かをするために必要な値の数を確認してください。

賢くではなく、明確にするためにもっと頑張ってください!

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

    return result;
}
6
rp.

フラグが1つのWordにまとめられている場合、 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
5
finnw

5つではなく数百万があった場合、Count()を避けて代わりにこれを行うことができます...

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

1つ以上のブール値がtrueと等しいことを意味する場合、次のようにできます。

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

Trueに等しい複数(2以上)のブール値が必要な場合は、試すことができます

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
faulty

Vilx-sバージョンよりも短くandい:

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

私は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チェックで述語を混在させることができるオーバーライドが含まれています。 [/プラグ]

2

私の頭の上から、この特定の例の簡単なアプローチ。 boolをint(0または1)に変換できます。次に、サームをループしてそれらを加算します。結果が2以上の場合、関数を実行できます。

2
Victor

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 )
{
  ....
}
1

私は今、はるかに優れたものを持っていますが、非常に短いです!

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

Intへのキャストと加算は機能するはずですが、それは少し見苦しく、一部の言語では不可能な場合があります。

のようなものはどうですか

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

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;

        }
1
John Sonmez

正確ではありません...しかし、それを行う別の方法があります:

if (
    (a && (b || c || d || e)) ||
    (b && (c || d || e)) ||
    (c && (d || e)) ||
    (d && e)
)
1
Vilx-
if (NumberOfTrue(new List<bool> { bool1, bool2, bool3, bool4 }) >= 2)
{
    // do stuff
}

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

あなたは言及しました

興味深いオプションの1つは、ブール値をバイトに格納し、右シフトを実行して元のバイトと比較することです。 if (myByte && (myByte >> 1))のようなもの

式はあなたが望む結果を与えるとは思いません(式は有効なC#ではないので、少なくともCセマンティクスを使用して):

_(myByte == 0x08)_の場合、式は1ビットしか設定されていなくてもtrueを返します。

if (myByte & (myByte >> 1))」を意味する場合、_(myByte == 0x0a)_の場合、2ビットが設定されていても式はfalseを返します。

ただし、Wordのビット数をカウントする方法は次のとおりです。

ビット調整ハック-ビットのカウント

あなたが検討するかもしれないバリエーションは、カーニハンのカウント方法を使用することですが、複数のビットが設定されているかどうかを知る必要があるだけなので、早期に脱出してください:

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

もちろん、ルックアップテーブルを使用することも悪い選択肢ではありません。

0
Michael Burr

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

//それらの複数が真

...

else

...

0

ほとんどの言語では、trueはゼロ以外の値に相当し、falseはゼロです。正確な構文はありませんが、擬似コードでは次のようになります。

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

5つの異なる値しかない場合、ビットをshortまたはintにパックし、それがゼロまたは1ビットの回答のいずれかであるかどうかを確認することにより、テストを簡単に実行できます。取得できる無効な数字は...

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

これにより、検索する6つの値が得られ、それらをルックアップテーブルに格納します。検索テーブルにない場合は、答えがあります。

これにより、簡単な答えが得られます。

 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)
 falseを返す; 
 trueを返す; 
} 

これは8ビットを大きく超えることはありませんが、5ビットしかありません。

0
Jens Wessling