Pergunta

Eu tenho um conjunto de cinco valores booleanos. Se mais de um destes são verdadeiras Eu quero excecute uma função particular. Qual é a maneira mais elegante que você pode pensar que me permita verificar essa condição em uma única instrução if ()? língua-alvo é C #, mas eu estou interessado em soluções em outros idiomas, bem como (contanto que nós não estamos falando sobre específica built-in funções).

Uma opção interessante é a de armazenar os booleans em um byte, fazer um deslocamento para a direita e comparar com o byte inicial. Algo como if(myByte && (myByte >> 1)) Mas isso exigiria a conversão dos booleans separadas para um byte (através de um BitArray?) E que parece um pouco (trocadilho intencional) desajeitado ... [editar] Desculpe, que deveria ter sido if(myByte & (myByte - 1)) [/ edit]

Nota: Este é, naturalmente, muito perto da clássica "contagem da população", "lado disso" ou "peso Hamming" problema de programação - mas não exatamente o mesmo. Eu não preciso de saber quantos dos bits são definidos, só se for mais de um. Minha esperança é que há uma maneira muito mais simples de fazer isso.

Foi útil?

Solução

Como cerca

  if ((bool1? 1:0) + (bool2? 1:0) + (bool3? 1:0) + 
      (bool4? 1:0) + (bool5? 1:0) > 1)
      // do something

ou um método generalizado seria ...

   public bool ExceedsThreshold(int threshold, IEnumerable<bool> bools)
    {
       int trueCnt = 0;
       foreach(bool b in bools)
          if (b && (++trueCnt > threshold)) 
              return true;
       return false;          
    } 

ou usando LINQ como sugerido por outras respostas:

    public bool ExceedsThreshold(int threshold, IEnumerable<bool> bools)
    { return bools.Count(b => b) > threshold; }

EDIT (para adicionar sugestão Joel Coehoorn: (Em .Net 2.x e posterior)

    public void ExceedsThreshold<T>(int threshold, 
                      Action<T> action, T parameter, 
                      IEnumerable<bool> bools)
    { if (ExceedsThreshold(threshold, bools)) action(parameter); }

ou na Net 3.5 e posterior:

    public void ExceedsThreshold(int threshold, 
            Action action, IEnumerable<bool> bools)
    { if (ExceedsThreshold(threshold, bools)) action(); }

ou como uma extensão para IEnumerable<bool>

  public static class IEnumerableExtensions
  {
      public static bool ExceedsThreshold<T> 
         (this IEnumerable<bool> bools, int threshold)
      { return bools.Count(b => b) > threshold; }
  }

uso, então, seria:

  var bools = new [] {true, true, false, false, false, false, true};
  if (bools.ExceedsThreshold(3))
      // code to execute  ...

Outras dicas

Eu estava indo para escrever a versão Linq, mas cinco ou mais pessoas bater-me a ele. Mas eu realmente gosto os parâmetros aproximar para evitar ter que manualmente nova-se um array. Então eu acho que o melhor híbrido é, com base na resposta do rp com o corpo substituir com o Linqness óbvia:

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

Muito bem claro para ler, e ao uso:

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

É tempo para a resposta LINQ obrigatório, que neste caso é realmente muito legal.

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

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

Gostaria apenas de lançá-los para ints e soma.

A menos que você está em um ciclo super apertado interior, que tem a vantagem de ser fácil de entender.

eu ia escrever uma função para receber qualquer número de valores booleanos. Seria retornar o número daqueles valores que são verdadeiras. Verifique o resultado para o número de valores que você precisa ser positivo para fazer algo.

O trabalho mais difícil de deixar claro, não inteligente!

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

    return result;
}

Se você quer dizer mais do que ou igual a um boolean equals a verdade, você poderia fazê-lo como

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

Se você precisar de mais de um (2 e acima) booleans igual a true, você pode tentar

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

Se havia milhões em vez de apenas 5 você poderia evitar Count () e fazer isso em vez ...

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

Se suas bandeiras são embalados em uma palavra, em seguida, Michael Burr solução vai funcionar. No entanto, o loop não é necessário:

int moreThanOneBitSet( unsigned int v)
{
    return (v & (v - 1)) != 0;
}

exemplo

 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

Shorter e mais feia do que a versão Vilx-s:

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

a partir do topo da minha cabeça, uma abordagem rápida para este exemplo específico; você pode converter o bool para um int (0 ou 1). circuito, em seguida, através de terma e adicioná-los para cima. se o resultado> = 2, então você pode executar a sua função.

Enquanto eu como LINQ, existem alguns buracos, como este problema.

Fazendo uma contagem é bom em geral, mas pode se tornar um problema quando os itens a sua contagem demorar um pouco para calcular / recuperar.

A Any () método de extensão é bom se você quer apenas para verificar se há algum, mas se você quiser verificar se há pelo menos há nenhuma construído em função que irá fazê-lo e ser preguiçoso.

No final, eu escrevi uma função para retornar verdadeiro se há pelo menos um certo número de itens na lista.

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

Para usar:

var query = bools.Where(b => b).AtLeast(2);

Isto tem a vantagem de não necessitar de avaliar todos os itens antes de retornar um resultado.

[plug] Meu projeto, NExtension contém pelo menos, atmost e substituições que permitem que você misturar o predicado com a pelo menos / mais cheque. [/ Plug]

A projeção para ints e soma deve funcionar, mas é um pouco feio e, em alguns idiomas podem não ser possível.

Como sobre algo como

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

Ou se você não se preocupam com o espaço, você poderia apenas precompute tabela verdade e usar os bools como índices:

if (morethanone[bool1][bool2][bool3][bool4][bool5]) {
 ... do something ...
}

eu faria algo como isso, usando o argumento 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 (NumberOfTrue(new List<bool> { bool1, bool2, bool3, bool4 }) >= 2)
{
    // do stuff
}

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

Não é exatamente bonita ... mas aqui está uma outra maneira de fazê-lo:

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

Eu tenho um muito, muito melhor um agora e muito curto!

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

Eu queria dar um C ++ 11 resposta modelo variádica.

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

simplesmente chamando-o como segue cria um método bastante elegante da contagem do número de bools.

if ( countBool( bool1, bool2, bool3 ) > 1 )
{
  ....
}

Na maioria das linguagens verdade é equivalente a um valor diferente de zero, enquanto falsa é zero. Eu não tenho sintaxe exata para você, mas em pseudo-código, o que acontece:

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

Se você só tem cinco valores diferentes, você pode facilmente fazer o teste embalando os bits em um curto ou um int e verificar para ver se é qualquer uma das respostas zero ou um pouco. Os números única inválidos que você poderia começar seria ..

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

Isto dá-lhe seis valores para procurar, colocá-los em uma tabela de pesquisa e se ele não está lá, você tem sua resposta.

Isto dá-lhe uma resposta simples.

   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)
         return false;
      return true;
   }

Esta não escala bem passado 8 bits, mas você só tem cinco.

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

// Mais de um deles são true

...

else

...

Você mencionou

Uma opção interessante é a de armazenar os booleans em um byte, fazer um deslocamento para a direita e comparar com o byte inicial. Algo como if (myByte && (myByte >> 1))

Eu não acho que a expressão lhe dará o resultado desejado (pelo menos usando a semântica C, uma vez que a expressão não é válido C #):

Se (myByte == 0x08), então a expressão retornará verdade mesmo que há apenas um conjunto de bits.

Se você quis dizer "if (myByte & (myByte >> 1))", em seguida, se (myByte == 0x0a) a expressão retornará false mesmo que existem 2 bits definidos.

Mas aqui estão algumas técnicas para contar o número de bits de uma palavra:

Bit girando Hacks - Contagem pedaços

Uma variação que você pode considerar é usar o método de contagem de Kernighan, mas socorrer cedo desde que você só precisa saber se há mais de um conjunto de bits:

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

Claro, usando uma tabela de pesquisa não é uma má opção.

Recentemente eu estava tendo esse mesmo problema, onde tive três valores booleanos, que eu precisava para verificar que apenas 1 deles era verdade de cada vez. Para isso eu usei o operador XOR da seguinte forma:

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

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

Este código irá lançar um erro como a e b são ambos true.

Para referência: http://www.dotnetperls.com/xor

Eu apenas encontrei o operador XOR em C # se alguém sabe de qualquer pit cai desta estratégia, por favor me avise.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top