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

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

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

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

Ответы [ 22 ]

2 голосов
/ 19 декабря 2008

Хотя мне нравится 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. [/ Штекер]

1 голос
/ 18 декабря 2008
if (NumberOfTrue(new List<bool> { bool1, bool2, bool3, bool4 }) >= 2)
{
    // do stuff
}

int NumberOfTrue(IEnumerable<bool> bools)
{
    return bools.Count(b => b);
}
1 голос
/ 18 декабря 2008

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

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

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

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

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

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 ...
}
1 голос
/ 10 июня 2017

Я хотел дать ответ на вариабельный шаблон 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 )
{
  ....
}
1 голос
/ 18 декабря 2008

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

bool[] bools = { b1, b2, b3, b4, b5 };
if (bools.Where(x => x).Count() > 1)
{
   //do stuff
}
0 голосов
/ 24 декабря 2013

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

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

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

if ((bool1 * 1) + (bool2 * 1) + (bool3 * 1) > 2)
{
    //statements here
}
0 голосов
/ 18 декабря 2008

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

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

...

еще

...

...