Domanda

Ho una serie di cinque valori booleani. Se più di uno di questi sono vere voglio eseguirli una particolare funzione. Qual è il modo più elegante si può pensare che mi permettesse di verificare questa condizione in una singola istruzione if ()? lingua di destinazione è C #, ma io sono interessato a soluzioni in altre lingue, nonché (a patto che non stiamo parlando di specifiche funzioni built-in).

Una possibilità interessante è quella di memorizzare i booleani in un byte, fare uno spostamento a destra e confrontarlo con il byte originale. Qualcosa di simile if(myByte && (myByte >> 1)) Ma questo richiederebbe la conversione dei booleani separati per un byte (tramite un BitArray?) E che sembra un po '(gioco di parole) maldestra ... [modifica] Ci dispiace, che avrebbe dovuto essere if(myByte & (myByte - 1)) [/ modifica]

Nota: Questo è, naturalmente, molto vicino al classico "conteggio della popolazione", "lateralmente oltre" o "peso di Hamming" problema di programmazione - ma non proprio la stessa cosa. Non ho bisogno di sapere quanti dei bit sono impostati, solo se si tratta di più di uno. La mia speranza è che c'è un modo molto più semplice per ottenere questo risultato.

È stato utile?

Soluzione

Come su

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

o di un metodo generalizzato sarebbe ...

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

o utilizzando LINQ come suggerito da altre risposte:

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

EDIT (per aggiungere Joel Coehoorn suggerimento: (In .Net 2.xe versioni successive)

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

o in .Net 3.5 e versioni successive:

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

o come estensione di IEnumerable<bool>

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

Utilizzo sarebbe allora:

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

Altri suggerimenti

stavo per scrivere la versione LINQ, ma cinque o giù di lì la gente mi ha battuto ad esso. Ma mi piace molto l'approccio params per evitare di dover nuovo manualmente un array. Quindi penso che il miglior ibrido è, in base alla risposta del rp con il corpo di sostituire con l'ovvio Linqness:

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

Splendidamente chiaro da leggere, e di utilizzare:

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

E 'tempo per la risposta LINQ obbligatoria, che in questo caso è in realtà abbastanza ordinata.

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

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

Vorrei solo le precipitava int e somma.

A meno che non sei in un ciclo interno super stretto, che ha il vantaggio di essere facile da capire.

mi piacerebbe scrivere una funzione per ricevere un numero qualsiasi di valori booleani. Sarebbe restituire il numero di quei valori che sono vere. Controllare il risultato per il numero di valori è necessario essere positiva per fare qualcosa.

lavorare di più per mettere in chiaro, non è intelligente!

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

    return result;
}

se si intende superiore o uguale a un valore booleano è uguale al vero, si potrebbe fare come

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

Se avete bisogno di più di una (2 e sopra) booleani pari al vero, si può provare

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 non ci fossero milioni invece di solo 5 si poteva evitare Count () e fare questo, invece ...

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

Se le bandiere sono confezionate in una sola parola, allora La soluzione di Michael Burr funzionerà. Tuttavia, il ciclo non è necessario:

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

Esempio

 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 più brutto di versione Vilx-s:

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

dalla parte superiore della mia testa, un approccio rapido per questo esempio specifico; si potrebbe convertire il bool per un int (0 o 1). poi scorrere Therm e li sommano. se il risultato> = 2, allora è possibile eseguire la funzione.

Mentre mi piace LINQ, ci sono alcuni fori in esso, come questo problema.

Fare un conteggio va bene in generale, ma può diventare un problema quando gli elementi vostro conteggio prendere un po 'per il calcolo / recuperare.

L'Qualsiasi () metodo di estensione va bene se si desidera solo per verificare la presenza di qualsiasi, ma se si desidera controllare per almeno non c'è costruito in funzione che lo farà e essere pigro.

Alla fine, ho scritto una funzione per restituire vero se ci sono almeno un certo numero di elementi nell'elenco.

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

Per usare:

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

Questo ha il vantaggio di non aver bisogno di valutare tutti gli elementi prima di restituire un risultato.

[Plug] Il mio progetto, NExtension contiene Atleast, atmost e le sostituzioni che permettono di mixare in il predicato con l'/ la maggior controllo atleast. [/ Spina]

Casting per int e sommando dovrebbe funzionare, ma è un po 'brutto e in alcune lingue potrebbe non essere possibile.

Che ne dite qualcosa come

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

O se non si cura di spazio, si può solo precompute la tabella di verità e utilizzare le Caccio come indici:

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

Vorrei fare qualcosa di simile, utilizzando l'argomento 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);
}

Non esattamente abbastanza ... ma ecco un altro modo per farlo:

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

Ho un molto molto meglio una adesso e molto corto!

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

ho voluto dare un C ++ 11 variadic risposta modello.

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

semplicemente chiamando come segue crea un metodo piuttosto elegante di contare il numero di Caccio.

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

Nella maggior parte delle lingue vero è equivalente ad un valore diverso da zero, mentre falso è pari a zero. Non ho sintassi esatta per te, ma in pseudo-codice, che dire:

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

Se avete solo cinque valori diversi, si può facilmente fare il test imballando i bit per un breve o un int e controllo per vedere se è una delle risposte zero o una bit. Gli unici numeri non validi si potrebbe ottenere sarebbe ..

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

Questo vi dà sei valori di ricerca, di metterli in una tabella di ricerca e se non è in là, si ha la risposta.

Questo vi dà una risposta semplice.

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

Questo non scalare ben oltre 8 bit, ma hai solo cinque.

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

// Più di uno di loro sono vere

...

altro

...

Hai menzionato

  

Una possibilità interessante è quella di memorizzare i booleani in un byte,   fare uno spostamento a destra e confrontarlo con il byte originale.   Qualcosa di simile if (myByte && (myByte >> 1))

Non credo che l'espressione vi darà il risultato desiderato (almeno utilizzando la semantica C, dal momento che l'espressione non è valido C #):

Se (myByte == 0x08), allora l'espressione restituisce vero anche se c'è solo un bit impostato.

Se si intende "if (myByte & (myByte >> 1))" allora se (myByte == 0x0a) l'espressione restituisce false, anche se ci sono 2 bit impostati.

Ma qui sono alcune tecniche per contare il numero di bit in una parola:

Bit girarsi Hacks - bit di conteggio

Una variazione si potrebbe prendere in considerazione è quello di utilizzare il metodo di conteggio di Kernighan, ma tirare fuori dai guai presto dal momento che solo bisogno di sapere se c'è più di un set bit:

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

Naturalmente, utilizzando una tabella di ricerca non è una cattiva scelta possibile.

Sono stato di recente avendo lo stesso problema, dove ho avuto tre valori booleani, che avevo bisogno di verificare che solo 1 di loro era vero in un momento. Per questo ho usato l'operatore XOR come segue:

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

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

Questo codice genera un errore come a e b sono entrambe vere.

Per riferimento: http://www.dotnetperls.com/xor

Ho appena trovato l'operatore XOR in C # se qualcuno sa di qualsiasi pozzo cade di questa strategia, per favore fatemelo sapere.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top