Изящно определить, является ли более одного логического значения «истинным»

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

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

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

70 голосов | спросил Ola Tuvesson 18 ThuEurope/Moscow2008-12-18T17:27:48+03:00Europe/Moscow12bEurope/MoscowThu, 18 Dec 2008 17:27:48 +0300 2008, 17:27:48

22 ответа


0

Как насчет

  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  ...
ответил Charles Bretana 18 ThuEurope/Moscow2008-12-18T17:37:00+03:00Europe/Moscow12bEurope/MoscowThu, 18 Dec 2008 17:37:00 +0300 2008, 17:37:00
0

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

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

Красиво понятный для чтения и использования:

if (Truth(m, n, o, p, q) > 2)
ответил Daniel Earwicker 18 ThuEurope/Moscow2008-12-18T18:53:56+03:00Europe/Moscow12bEurope/MoscowThu, 18 Dec 2008 18:53:56 +0300 2008, 18:53:56
0

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

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

return bools.Count(b => b == true) > 1;
ответил Garry Shutler 18 ThuEurope/Moscow2008-12-18T18:22:19+03:00Europe/Moscow12bEurope/MoscowThu, 18 Dec 2008 18:22:19 +0300 2008, 18:22:19
0

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

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

ответил recursive 18 ThuEurope/Moscow2008-12-18T17:34:27+03:00Europe/Moscow12bEurope/MoscowThu, 18 Dec 2008 17:34:27 +0300 2008, 17:34:27
0

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

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

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

    return result;
}
ответил rp. 18 ThuEurope/Moscow2008-12-18T18:01:28+03:00Europe/Moscow12bEurope/MoscowThu, 18 Dec 2008 18:01:28 +0300 2008, 18:01:28
0

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

public static bool MoreThanOne (IEnumerable<bool> booleans)
{
    return booleans.SkipWhile(b => !b).Skip(1).Any(b => b);
}
ответил Ian Mercer 6 J0000006Europe/Moscow 2010, 07:21:55
0

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

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
ответил finnw 10 Maypm11 2011, 19:45:27
0

Короче и страшнее, чем версия Vilx-s:

if (((a||b||c)&&(d||e))||((a||d)&&(b||c||e))||(b&&c)) {}
ответил some 18 ThuEurope/Moscow2008-12-18T20:43:19+03:00Europe/Moscow12bEurope/MoscowThu, 18 Dec 2008 20:43:19 +0300 2008, 20:43:19
0

если вы имеете в виду больше или равно одному логическому значению, равному 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
ответил faulty 18 ThuEurope/Moscow2008-12-18T17:35:39+03:00Europe/Moscow12bEurope/MoscowThu, 18 Dec 2008 17:35:39 +0300 2008, 17:35:39
0

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

ответил Victor 18 ThuEurope/Moscow2008-12-18T17:36:04+03:00Europe/Moscow12bEurope/MoscowThu, 18 Dec 2008 17:36:04 +0300 2008, 17:36:04
0

Хотя мне нравится 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);

Преимущество этого заключается в отсутствии необходимости оценивать все элементы перед возвратом результата.

[Plug] Мой проект, NExtension содержит AtLeast, AtMost и переопределения, которые позволяют смешивать предикат с проверкой AtLeast /Most. [/Штекер]

ответил Cameron MacFarland 19 FriEurope/Moscow2008-12-19T18:08:42+03:00Europe/Moscow12bEurope/MoscowFri, 19 Dec 2008 18:08:42 +0300 2008, 18:08:42
0

Приведение к целым числам и суммирование должны работать, но это немного уродливо и в некоторых языках может быть невозможно.

Как насчет чего-то вроде

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

Или, если вас не волнует пространство, вы можете просто предварительно вычислить таблицу истинности и использовать bool в качестве индексов:

if (morethanone[bool1][bool2][bool3][bool4][bool5]) {
 ... do something ...
}
ответил frankodwyer 18 ThuEurope/Moscow2008-12-18T17:43:43+03:00Europe/Moscow12bEurope/MoscowThu, 18 Dec 2008 17:43:43 +0300 2008, 17:43:43
0

Я бы сделал что-то подобное, используя аргумент 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;

        }
ответил John Sonmez 18 ThuEurope/Moscow2008-12-18T17:48:31+03:00Europe/Moscow12bEurope/MoscowThu, 18 Dec 2008 17:48:31 +0300 2008, 17:48:31
0
if (NumberOfTrue(new List<bool> { bool1, bool2, bool3, bool4 }) >= 2)
{
    // do stuff
}

int NumberOfTrue(IEnumerable<bool> bools)
{
    return bools.Count(b => b);
}
ответил AndreasN 18 ThuEurope/Moscow2008-12-18T18:04:41+03:00Europe/Moscow12bEurope/MoscowThu, 18 Dec 2008 18:04:41 +0300 2008, 18:04:41
0

Не совсем красиво ... но вот еще один способ сделать это:

if (
    (a && (b || c || d || e)) ||
    (b && (c || d || e)) ||
    (c && (d || e)) ||
    (d && e)
)
ответил Vilx- 18 ThuEurope/Moscow2008-12-18T17:48:33+03:00Europe/Moscow12bEurope/MoscowThu, 18 Dec 2008 17:48:33 +0300 2008, 17:48:33
0

У меня сейчас намного лучше и очень короткий!

bool[] bools = { b1, b2, b3, b4, b5 };
if (bools.Where(x => x).Count() > 1)
{
   //do stuff
}
ответил John Sonmez 18 ThuEurope/Moscow2008-12-18T18:34:46+03:00Europe/Moscow12bEurope/MoscowThu, 18 Dec 2008 18:34:46 +0300 2008, 18:34:46
0

Я хотел дать ответ на вариабельный шаблон в 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;
}

просто вызывая его следующим образом, создаем довольно элегантный метод подсчета количества bools.

if ( countBool( bool1, bool2, bool3 ) > 1 )
{
  ....
}
ответил Scott Aron Bloom 10 J0000006Europe/Moscow 2017, 23:30:55
0

В большинстве языков значение true эквивалентно ненулевому значению, а значение false равно нулю. У меня нет точного синтаксиса для вас, но в псевдокоде, как насчет:

if ((bool1 * 1) + (bool2 * 1) + (bool3 * 1) > 2)
{
    //statements here
}
ответил Bork Blatt 18 ThuEurope/Moscow2008-12-18T17:36:09+03:00Europe/Moscow12bEurope/MoscowThu, 18 Dec 2008 17:36:09 +0300 2008, 17:36:09
0

Если у вас есть только пять различных значений, вы можете легко выполнить тестирование, упаковав биты в 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)
   {
      последний короткий 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)
         вернуть ложь;
      вернуть истину;
   }

Это не масштабируется выше 8 бит, но у вас есть только пять.

ответил 18 ThuEurope/Moscow2008-12-18T21:59:56+03:00Europe/Moscow12bEurope/MoscowThu, 18 Dec 2008 21:59:56 +0300 2008, 21:59:56
0

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

//Более одного из них верны

...

еще

...

ответил Partha Choudhury 18 ThuEurope/Moscow2008-12-18T21:19:36+03:00Europe/Moscow12bEurope/MoscowThu, 18 Dec 2008 21:19:36 +0300 2008, 21:19:36
0

Вы упомянули

  

Один интересный вариант - хранить логические значения в байте,   сделайте правильный сдвиг и сравните с исходным байтом.   Что-то вроде if (myByte && (myByte >> 1))

Я не думаю, что выражение даст вам желаемый результат (по крайней мере, с использованием семантики C, поскольку выражение не является допустимым C #):

Если (myByte == 0x08), выражение вернет true, даже если установлен только один бит.

Если вы имели в виду "if (myByte & (myByte >> 1))", то если (myByte == 0x0a) выражение вернет false, даже если установлено 2 бита.

Но вот некоторые методы подсчета количества битов в слове:

Взлом битовых комбинаций - подсчет битов

Вариант, который вы могли бы рассмотреть, - это использовать метод подсчета Кернигана, но выручить пораньше, поскольку вам нужно только знать, установлено ли более одного бита:

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

Конечно, использование таблицы поиска тоже неплохой вариант.

ответил Michael Burr 19 FriEurope/Moscow2008-12-19T05:20:48+03:00Europe/Moscow12bEurope/MoscowFri, 19 Dec 2008 05:20:48 +0300 2008, 05:20:48
0

Недавно у меня возникла такая же проблема, когда у меня было три логических значения, и мне нужно было проверить, что только 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

Я только что нашел оператор xor в C #, если кто-нибудь знает о каких-либо проблемах этой стратегии, пожалуйста, дайте мне знать.

ответил dmoore1181 24 TueEurope/Moscow2013-12-24T23:26:15+04:00Europe/Moscow12bEurope/MoscowTue, 24 Dec 2013 23:26:15 +0400 2013, 23:26:15

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132