Изящно определите, является ли более одного логического значения истинным - PullRequest
71 голосов
/ 18 декабря 2008

У меня есть набор из пяти логических значений. Если более одного из них являются истинными, я хочу выполнить конкретную функцию. Какой самый элегантный способ, который вы можете себе представить, позволил бы мне проверить это условие в одном операторе if ()? Целевым языком является C #, но мне интересны решения и на других языках (если мы не говорим о конкретных встроенных функциях).

Один интересный вариант - сохранить логические значения в байте, выполнить сдвиг вправо и сравнить с исходным байтом. Что-то вроде if(myByte && (myByte >> 1)) Но для этого потребуется преобразовать отдельные логические значения в байт (через bitArray?), И это кажется немного (каламбур) неуклюжим ... [edit] Извините, это должно было быть if(myByte & (myByte - 1)) [/ edit]

Примечание: это, конечно, очень близко к классической проблеме программирования "подсчет населения", "сложение вбок" или "вес Хэмминга" - но не совсем то же самое. Мне не нужно знать, сколько битов установлено, только если их больше одного. Я надеюсь, что есть гораздо более простой способ сделать это.

Ответы [ 22 ]

114 голосов
/ 18 декабря 2008

Я собирался написать версию Linq, но пять или около того человек меня опередили. Но мне действительно нравится подход params, чтобы избежать необходимости вручную создавать массив. Так что я думаю, что лучший гибрид, основанный на ответе rp с заменой тела на очевидное Linqness:

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

Красиво ясно, чтобы читать и использовать:

if (Truth(m, n, o, p, q) > 2)
84 голосов
/ 18 декабря 2008

Как насчет

  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  ...
18 голосов
/ 18 декабря 2008

Пришло время для обязательного ответа LINQ, который в данном случае на самом деле довольно аккуратный.

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

return bools.Count(b => b == true) > 1;
16 голосов
/ 18 декабря 2008

Я бы просто бросил их в целые и сумма.

Если вы не находитесь в сверхжестком внутреннем цикле, то преимущество в том, что его легко понять.

6 голосов
/ 18 декабря 2008

Я бы написал функцию для получения любого числа логических значений. Это вернуло бы число тех значений, которые являются истинными. Проверьте результат на количество значений, которые вы должны быть положительными, чтобы что-то сделать.

Работай усерднее, чтобы было понятно, а не умно!

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

    return result;
}
5 голосов
/ 06 июня 2010

Если бы было не 5, а миллионы, то вы могли бы избежать Count () и сделать это вместо ...

public static bool MoreThanOne (IEnumerable<bool> booleans)
{
    return booleans.SkipWhile(b => !b).Skip(1).Any(b => b);
}
5 голосов
/ 10 мая 2011

Если ваши флаги упакованы в одно слово, тогда Решение Майкла Барра будет работать. Однако цикл не обязателен:

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 голосов
/ 18 декабря 2008

Короче и хуже, чем версия Vilx-s:

if (((a||b||c)&&(d||e))||((a||d)&&(b||c||e))||(b&&c)) {}
4 голосов
/ 18 декабря 2008

если вы имеете в виду больше или равно одному логическому значению, равному 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
2 голосов
/ 18 декабря 2008

от макушки головы, быстрый подход к этому конкретному примеру; Вы можете конвертировать bool в int (0 или 1). затем переберите терм и сложите их. если результат> = 2, вы можете выполнить свою функцию.

...