web-dev-qa-db-fra.com

Déterminez avec élégance si plus d'un booléen est "vrai"

J'ai un ensemble de cinq valeurs booléennes. Si plusieurs d'entre elles sont vraies, je veux exécuter une fonction particulière. Quelle est la façon la plus élégante dont vous pouvez penser qui me permettrait de vérifier cette condition dans une seule instruction if ()? Le langage cible est C # mais je suis également intéressé par des solutions dans d'autres langages (tant que nous ne parlons pas de fonctions intégrées spécifiques).

Une option intéressante consiste à stocker les booléens dans un octet, à effectuer un décalage à droite et à comparer avec l'octet d'origine. Quelque chose comme if(myByte && (myByte >> 1)) Mais cela nécessiterait de convertir les booléens séparés en octets (via un bitArray?) Et cela semble un peu (jeu de mots destiné) maladroit ... [modifier] Désolé, cela devrait ont étéif(myByte & (myByte - 1))[/ edit]

Remarque: Ceci est bien sûr très proche du problème de programmation classique "dénombrement de la population", "addition latérale" ou "poids de Hamming" - mais pas tout à fait la même chose. Je n'ai pas besoin de savoir combien de bits sont définis, seulement si c'est plus d'un. J'espère qu'il existe un moyen beaucoup plus simple d'accomplir cela.

73
Ola Tuvesson

Que diriez-vous

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

ou une méthode généralisée serait ...

   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 en utilisant LINQ comme suggéré par d'autres réponses:

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

EDIT (pour ajouter la suggestion de Joel Coehoorn: (dans .Net 2.x et versions ultérieures)

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

ou dans .Net 3.5 et versions ultérieures:

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

ou comme extension à IEnumerable<bool>

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

l'utilisation serait alors:

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

J'allais écrire la version Linq, mais environ cinq personnes m'ont battu. Mais j'aime beaucoup l'approche params pour éviter d'avoir à renouveler manuellement un tableau. Je pense donc que le meilleur hybride est, basé sur la réponse de rp avec le corps remplacé par le Linqness évident:

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

Magnifiquement clair à lire et à utiliser:

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

Il est temps pour la réponse LINQ obligatoire, qui dans ce cas est en fait assez soignée.

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

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

Je les jetterais juste en pouces et en somme.

À moins que vous ne soyez dans une boucle intérieure super serrée, cela a l'avantage d'être facile à comprendre.

16
recursive

J'écrirais une fonction pour recevoir n'importe quel nombre de valeurs booléennes. Il retournerait le nombre de ces valeurs qui sont vraies. Vérifiez le résultat pour le nombre de valeurs dont vous avez besoin pour être positif pour faire quelque chose.

Travaillez plus dur pour être clair, pas intelligent!

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

    return result;
}
6
rp.

Si vos drapeaux sont regroupés dans un seul mot, alors la solution de Michael Burr fonctionnera. Cependant, la boucle n'est pas nécessaire:

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

exemple

 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
5
finnw

S'il y avait des millions au lieu de 5, vous pourriez éviter Count () et le faire à la place ...

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

si vous voulez dire plus ou égal à un booléen est égal à vrai, vous pouvez le faire comme

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

Si vous avez besoin de plus d'un booléen (2 et plus) égal à vrai, vous pouvez essayer

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
5
faulty

Plus court et plus laid que la version Vilx-s:

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

Bien que j'aime LINQ, il y a quelques trous, comme ce problème.

Faire un comptage est très bien en général, mais peut devenir un problème lorsque les éléments que votre comptage met un certain temps à calculer/récupérer.

La méthode d'extension Any () est très bien si vous voulez juste en vérifier, mais si vous voulez au moins vérifier qu'il n'y a pas de fonction intégrée qui le fera et sera paresseuse.

Au final, j'ai écrit une fonction pour retourner true s'il y a au moins un certain nombre d'éléments dans la liste.

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

Utiliser:

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

Cela a l'avantage de ne pas avoir besoin d'évaluer tous les éléments avant de retourner un résultat.

[Plug] Mon projet, NExtension contient AtLeast, AtMost et des substitutions qui vous permettent de mélanger le prédicat avec la vérification AtLeast/Most. [/Prise de courant]

2
Cameron MacFarland

du haut de ma tête, une approche rapide pour cet exemple spécifique; vous pouvez convertir le booléen en entier (0 ou 1). puis bouclez à travers therm et ajoutez-les. si le résultat> = 2, vous pouvez exécuter votre fonction.

2
Victor

Je voulais donner une réponse de modèle variadique 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;
}

simplement l'appeler comme suit crée une méthode assez élégante pour compter le nombre de bools.

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

J'en ai un bien meilleur maintenant et très court!

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

La conversion en ints et la sommation devraient fonctionner, mais c'est un peu moche et dans certaines langues, cela peut ne pas être possible.

Que diriez-vous de quelque chose comme

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

Ou si vous ne vous souciez pas de l'espace, vous pouvez simplement précalculer la table de vérité et utiliser les bools comme indices:

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

Je ferais quelque chose comme ça, en utilisant l'argument 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
John Sonmez

Pas exactement joli ... mais voici une autre façon de le faire:

if (
    (a && (b || c || d || e)) ||
    (b && (c || d || e)) ||
    (c && (d || e)) ||
    (d && e)
)
1
Vilx-
if (NumberOfTrue(new List<bool> { bool1, bool2, bool3, bool4 }) >= 2)
{
    // do stuff
}

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

Vous avez mentionné

Une option intéressante consiste à stocker les booléens dans un octet, à effectuer un décalage à droite et à comparer avec l'octet d'origine. Quelque chose comme if (myByte && (myByte >> 1))

Je ne pense pas que cette expression vous donnera le résultat souhaité (au moins en utilisant la sémantique C, car l'expression n'est pas C # valide):

Si (myByte == 0x08), Alors l'expression retournera true même s'il n'y a qu'un seul bit défini.

Si vous vouliez dire "if (myByte & (myByte >> 1))" alors si (myByte == 0x0a) L'expression retournera false même si 2 bits sont définis.

Mais voici quelques techniques pour compter le nombre de bits dans un mot:

Bit Twiddling Hacks - Compter les bits

Une variante que vous pourriez envisager est d'utiliser la méthode de comptage de Kernighan, mais renflouez-vous tôt car il vous suffit de savoir s'il y a plus d'un ensemble 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);
}

Bien sûr, l'utilisation d'une table de recherche n'est pas non plus une mauvaise option.

0
Michael Burr

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

// Plus d'un d'entre eux sont vrais

...

else

...

0
Partha Choudhury

Dans la plupart des langues, true équivaut à une valeur non nulle tandis que false est nul. Je n'ai pas de syntaxe exacte pour vous, mais en pseudo-code, qu'en est-il:

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

Si vous n'avez que cinq valeurs différentes, vous pouvez facilement faire le test en compressant les bits dans un court ou un entier et en vérifiant si c'est l'une des réponses zéro ou un bit. Les seuls numéros invalides que vous pourriez obtenir seraient ..

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

Cela vous donne six valeurs à rechercher, les mettre dans une table de recherche et si ce n'est pas là, vous avez votre réponse.

Cela vous donne une réponse simple.

 public statique booléen 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; 
} 

Cela n'évolue pas bien au-delà de 8 bits, mais vous n'en avez que cinq.

0
Jens Wessling