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.
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 ...
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)
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;
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.
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;
}
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
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);
}
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
Plus court et plus laid que la version Vilx-s:
if (((a||b||c)&&(d||e))||((a||d)&&(b||c||e))||(b&&c)) {}
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]
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.
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 )
{
....
}
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
}
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 ...
}
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;
}
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)
)
if (NumberOfTrue(new List<bool> { bool1, bool2, bool3, bool4 }) >= 2)
{
// do stuff
}
int NumberOfTrue(IEnumerable<bool> bools)
{
return bools.Count(b => b);
}
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.
if ((b1.CompareTo (false) + b2.CompareTo (false) + b3.CompareTo (false) + ...)> 1)
// Plus d'un d'entre eux sont vrais
...
else
...
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
}
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.