J'ai quatre bool
valeurs:
bool bValue1;
bool bValue2;
bool bValue3;
bool bValue4;
Les valeurs acceptables sont:
Scenario 1 | Scenario 2 | Scenario 3
bValue1: true | true | true
bValue2: true | true | false
bValue3: true | true | false
bValue4: true | false | false
Ainsi, par exemple, ce scénario n'est pas acceptable:
bValue1: false
bValue2: true
bValue3: true
bValue4: true
Pour le moment, j'ai élaboré cette if
déclaration pour détecter les mauvais scénarios:
if(((bValue4 && (!bValue3 || !bValue2 || !bValue1)) ||
((bValue3 && (!bValue2 || !bValue1)) ||
(bValue2 && !bValue1) ||
(!bValue1 && !bValue2 && !bValue3 && !bValue4))
{
// There is some error
}
Cette logique de déclaration peut-elle être améliorée / simplifiée?
c++
if-statement
Andrew Truckle
la source
la source
if
déclaration complexe . De plus, comme il s'agit d'indicateurs booléens, vous pouvez modéliser chaque scénario en tant que constante et la comparer.if (!((bValue1 && bValue2 && bValue3) || (bValue1 && !bValue2 && !bValue3 && !bValue4)))
bool scenario1 = bValue1 && bValue2 && bValue3 && bValue4;
Réponses:
Je viserais la lisibilité: vous n'avez que 3 scénarios, traitez-les avec 3 ifs distincts:
Facile à lire et à déboguer, à mon humble avis. En outre, vous pouvez affecter une variable
whichScenario
tout en poursuivant leif
.Avec seulement 3 scénarios, je n'irais pas avec quelque chose comme "si les 3 premières valeurs sont vraies, je peux éviter de vérifier la quatrième valeur": cela va rendre votre code plus difficile à lire et à maintenir.
Pas une solution élégante
peut êtresûrement, mais dans ce cas, c'est ok: facile et lisible.Si votre logique devient plus compliquée, jetez ce code et envisagez d'utiliser quelque chose de plus pour stocker différents scénarios disponibles (comme Zladeck le suggère).
J'aime vraiment la première suggestion donnée dans cette réponse : facile à lire, pas sujette aux erreurs, maintenable
(Presque) hors sujet:
Je n'écris pas beaucoup de réponses ici sur StackOverflow. C'est vraiment drôle que la réponse acceptée ci-dessus soit de loin la réponse la plus appréciée de mon histoire (je n'ai jamais eu plus de 5 à 10 votes positifs avant je pense) alors qu'en fait ce n'est pas ce que je pense généralement être la «bonne» façon de le faire.
Mais la simplicité est souvent "la bonne façon de faire", beaucoup de gens semblent penser cela et je devrais le penser plus que moi :)
la source
valid
et en les séparant par||
, plutôt que de mutervalid
dans des blocs d'instructions séparés. Je ne peux pas mettre d'exemple dans le commentaire mais vous pouvez aligner verticalement les||
opérateurs le long de la gauche pour que cela soit très clair; les conditions individuelles sont déjà mises entre parenthèses autant qu'elles le doivent (pourif
), vous n'avez donc pas besoin d'ajouter de caractères aux expressions au-delà de ce qui existe déjà.if($bValue1)
car cela doit toujours être vrai, permettant techniquement une amélioration mineure des performances (bien que nous parlions ici de quantités négligeables).bValue4
Je viserais la simplicité et la lisibilité.
Assurez-vous de remplacer les noms des scénarios ainsi que les noms des indicateurs par quelque chose de descriptif. Si cela a du sens pour votre problème spécifique, vous pouvez envisager cette alternative:
Ce qui est important ici, ce n'est pas la logique des prédicats. Il décrit votre domaine et exprime clairement votre intention. La clé ici est de donner de bons noms à toutes les entrées et variables intermédiaires. Si vous ne trouvez pas de bons noms de variables, cela peut être le signe que vous décrivez le problème de la mauvaise manière.
la source
Nous pouvons utiliser une carte de Karnaugh et réduire vos scénarios à une équation logique. J'ai utilisé le solveur de carte Karnaugh en ligne avec circuit pour 4 variables.
Cela donne:
Changer
A, B, C, D
pourbValue1, bValue2, bValue3, bValue4
, ce n'est rien d'autre que:Ainsi, votre
if
déclaration devient:true
.true
scénarios à une équation logique, ajouter des commentaires pertinents indiquant lestrue
scénarios est une bonne pratique.la source
//!(ABC + AB'C'D') (By K-Map logic)
. Ce serait un bon moment pour le développeur d'apprendre K-Maps s'il ne les connaît pas déjà.E
etF
conditions et 4 nouveaux scénarios? Combien de temps faut-il pour mettre à jourif
correctement cette déclaration? Comment la revue de code vérifie-t-elle si elle est correcte ou non? Le problème n'est pas du côté technique mais du côté "business".A
:ABC + AB'C'D' = A(BC + B'C'D')
(cela peut même être pris en compte mêmeA(B ^ C)'(C + D')
si je ferais attention d'appeler cela «simplification»).La vraie question ici est: que se passe-t-il lorsqu'un autre développeur (ou même auteur) doit changer ce code quelques mois plus tard.
Je suggérerais de modéliser cela sous forme d'indicateurs de bits:
S'il y a beaucoup plus de scénarios ou plus d'indicateurs, une approche de table est plus lisible et extensible que l'utilisation d'indicateurs. La prise en charge d'un nouveau scénario ne nécessite qu'une autre ligne dans le tableau.
la source
SCENARIO_2 = true << 3 | true << 2 | true << 1 | false;
2: éviter les variables SCENARIO_X puis stocker tous les scénarios disponibles dans un fichier<std::set<int>
. L'ajout d'un scénario va être juste quelque chose commemySet.insert( true << 3 | false << 2 | true << 1 | false;
peut-être un peu exagéré pour seulement 3 scénarios, OP a accepté la solution rapide, sale et facile que j'ai suggérée dans ma réponse.std::find
?).scenario
valeur me semblent inutilement sujettes à des erreurs.Ma réponse précédente est déjà la réponse acceptée, j'ajoute ici quelque chose qui me semble à la fois lisible, facile et dans ce cas ouvert à de futures modifications:
En commençant par la réponse @ZdeslavVojkovic (que je trouve assez bonne), j'ai trouvé ceci:
Voyez-le au travail ici
Eh bien, c'est la solution "élégante et maintenable" (IMHO) que je vise habituellement, mais vraiment, pour le cas OP, ma précédente réponse "bunch of ifs" correspond mieux aux exigences OP, même si elle n'est ni élégante ni maintenable.
la source
Je voudrais également proposer une autre approche.
Mon idée est de convertir les booléens en un entier, puis de comparer à l'aide de modèles variadiques:
Remarquez comment ce système peut prendre en charge jusqu'à 32 booléens en entrée. remplacer le
unsigned
parunsigned long long
(ouuint64_t
) augmente la prise en charge à 64 cas. Si vous n'aimez pas leif (summary != 0b1111u && summary != 0b1110u && summary != 0b1000u)
, vous pouvez également utiliser une autre méthode de modèle variadique:la source
bitmap_from_bools
, oubools_to_bitmap
?bools_to_unsigned
. Bitmap est un bon mot-clé; édité.summary!= 0b1111u &&...
.a != b || a != c
est toujours vrai sib != c
Voici une version simplifiée:
Notez bien sûr que cette solution est plus obscurcie que l'original, sa signification peut être plus difficile à comprendre.
Mise à jour: MSalters dans les commentaires a trouvé une expression encore plus simple:
la source
simple
est en effet un terme vague. Beaucoup de gens le comprennent dans ce contexte comme plus simple à comprendre pour le développeur et non pour le compilateur pour générer du code, donc plus détaillé peut en effet être plus simple.Pensez à traduire vos tableaux aussi directement que possible dans votre programme. Conduisez le programme hors de la table, au lieu de l'imiter avec la logique.
maintenant
cela encode directement que possible votre table de vérité dans le compilateur.
Exemple en direct .
Vous pouvez également utiliser
std::any_of
directement:le compilateur peut incorporer le code, éliminer toute itération et créer sa propre logique pour vous. Pendant ce temps, votre code reflète exactement comment vous avez conçu le problème.
la source
Je ne donne ma réponse ici que dans les commentaires que quelqu'un a suggéré de montrer ma solution. Je tiens à remercier tout le monde pour leurs idées.
Au final, j'ai choisi d'ajouter trois nouvelles
boolean
méthodes «scénario» :Ensuite, j'ai pu appliquer ceux de ma routine de validation comme ceci:
Dans mon application en direct, les 4 valeurs booléennes sont en fait extraites de a
DWORD
qui contient 4 valeurs encodées.Merci encore à tous.
la source
INCLUDE_ITEM1
mieux nommer etc. et que vous êtes tous bons. :)Je ne vois aucune réponse disant de nommer les scénarios, bien que la solution du PO fasse exactement cela.
Pour moi, il est préférable d'encapsuler le commentaire de chaque scénario dans un nom de variable ou un nom de fonction. Vous êtes plus susceptible d'ignorer un commentaire qu'un nom, et si votre logique change à l'avenir, vous êtes plus susceptible de changer un nom qu'un commentaire. Vous ne pouvez pas refactoriser un commentaire.
Si vous prévoyez de réutiliser ces scénarios en dehors de votre fonction (ou si vous le souhaitez), créez une fonction qui indique ce qu'elle évalue (
constexpr
/noexcept
facultatif mais recommandé):Faites ces méthodes de classe si possible (comme dans la solution d'OP). Vous pouvez utiliser des variables à l'intérieur de votre fonction si vous ne pensez pas réutiliser la logique:
Le compilateur réglera très probablement que si bValue1 est faux, tous les scénarios sont faux. Ne vous inquiétez pas de le rendre rapide, juste correct et lisible. Si vous profilez votre code et trouvez qu'il s'agit d'un goulot d'étranglement parce que le compilateur a généré du code sous-optimal à -O2 ou supérieur, essayez de le réécrire.
la source
Manière AC / C ++
Cette approche est évolutive comme si le nombre de conditions valides augmentait, vous en ajoutiez facilement plus à la liste des scénarios.
la source
true
. Un compilateur qui utilise «tout ce qui n'est pas nul est vrai» provoque l'échec de ce code. Notez quetrue
doit être converti en1
, il n'a tout simplement pas besoin d'être stocké en tant que tel.2 is not equal to true but evaluates to true
, mon code ne force pasint 1 = true
et fonctionne tant que tous les vrais sont convertis en la même valeur int, alors voici ma question: Pourquoi le compilateur devrait-il agir au hasard lors de la conversion fidèle à l'int sous-jacent, pouvez-vous s'il vous plaît élaborer plus?memcmp
test de conditions booléennes n'est pas la méthode C ++, et je doute plutôt que ce soit une méthode C établie non plus.Il est facile de remarquer que les deux premiers scénarios sont similaires - ils partagent la plupart des conditions. Si vous voulez sélectionner dans quel scénario vous vous trouvez en ce moment, vous pouvez l'écrire comme ceci (c'est une solution modifiée de @ gian-paolo ):
En allant plus loin, vous pouvez remarquer que le premier booléen doit être toujours vrai, ce qui est une condition d'entrée, vous pouvez donc vous retrouver avec:
Encore plus, vous pouvez maintenant clairement voir que bValue2 et bValue3 sont quelque peu connectés - vous pouvez extraire leur état vers certaines fonctions externes ou variables avec un nom plus approprié (ce n'est pas toujours facile ou approprié cependant):
Faire de cette façon présente certains avantages et inconvénients:
Si vous prévoyez qu'il y aura des changements dans la logique ci-dessus, vous devriez utiliser une approche plus simple comme présentée par @ gian-paolo .
Sinon, si ces conditions sont bien établies, et sont des sortes de «règles solides» qui ne changeront jamais, considérez mon dernier extrait de code.
la source
Comme suggéré par mch, vous pouvez faire:
où la première ligne couvre les deux premiers bons cas, et la deuxième ligne couvre le dernier.
Live Demo, où j'ai joué et ça passe tes valises.
la source
Une légère variation sur la bonne réponse de @ GianPaolo, que certains peuvent trouver plus facile à lire:
la source
Chaque réponse est trop complexe et difficile à lire. La meilleure solution à cela est une
switch()
déclaration. Il est à la fois lisible et facilite l'ajout / la modification de cas supplémentaires. Les compilateurs sont également bons pour optimiser lesswitch()
déclarations.Vous pouvez bien sûr utiliser des constantes et les OU ensemble dans les
case
instructions pour une meilleure lisibilité.la source
J'utiliserais également des variables de raccourci pour plus de clarté. Comme indiqué précédemment, le scénario 1 équivaut au scénario 2, car la valeur de bValue4 n'influence pas la vérité de ces deux scénarios.
alors votre expression devient:
Donner des noms significatifs aux variables MAJORTRUE et MAJORFALSE (ainsi qu'en fait à bValue * vars) aiderait beaucoup à la lisibilité et à la maintenance.
la source
Concentrez-vous sur la lisibilité du problème, pas sur l'énoncé «si» spécifique.
Bien que cela produise plus de lignes de code, et certains peuvent le considérer comme excessif ou inutile. Je suggérerais que l'abstraction de vos scénarios à partir des booléens spécifiques est le meilleur moyen de maintenir la lisibilité.
En divisant les choses en classes (n'hésitez pas à utiliser simplement des fonctions, ou tout autre outil que vous préférez) avec des noms compréhensibles - nous pouvons beaucoup plus facilement montrer la signification de chaque scénario. Plus important encore, dans un système avec de nombreuses pièces mobiles - il est plus facile de maintenir et de se joindre à vos systèmes existants (encore une fois, malgré la quantité de code supplémentaire impliquée).
la source
Cela dépend de ce qu'ils représentent.
Par exemple, si 1 est une clé, et 2 et 3 sont deux personnes qui doivent être d'accord (sauf si elles sont d'accord sur le fait
NOT
qu'elles ont besoin d'une troisième personne - 4 - pour confirmer), la plus lisible pourrait être:à la demande générale:
la source
bValue
.Faire une opération au niveau du bit semble très propre et compréhensible.
la source
Je désigne a, b, c, d pour plus de clarté et A, B, C, D pour les compléments
Équation
Utilisez toutes les équations qui vous conviennent.
la source
Facile
la source
Juste une préférence personnelle sur la réponse acceptée, mais j'écrirais:
la source
Tout d'abord, en supposant que vous ne pouvez modifier que la vérification du scénario, je me concentrerais sur la lisibilité et j'envelopperais simplement la vérification dans une fonction afin que vous puissiez simplement appeler
if(ScenarioA())
.Maintenant, en supposant que vous voulez / devez réellement optimiser cela, je recommanderais de convertir les booléens étroitement liés en nombres entiers constants et d'utiliser des opérateurs de bits sur eux
Cela rend l'expression des scénarios aussi simple que de lister ce qui en fait partie, vous permet d'utiliser une instruction switch pour passer à la bonne condition et de dérouter les autres développeurs qui n'ont jamais vu cela auparavant. (C # RegexOptions utilise ce modèle pour définir des indicateurs, je ne sais pas s'il existe un exemple de bibliothèque c ++)
la source
Les
if
s imbriqués pourraient être plus faciles à lire pour certaines personnes. Voici ma versionla source
bValue1
bloc, vous pouvez tout traiter comme une nouvelle page fraîche dans votre processus mental. Je parie que la manière d'aborder le problème peut être très personnelle ou même culturelle.Plusieurs réponses correctes ont été données à cette question, mais j'adopterais un point de vue différent: si le code semble trop compliqué, quelque chose ne va pas . Le code sera difficile à déboguer et plus susceptible d'être "à usage unique".
Dans la vraie vie, quand on trouve une situation comme celle-ci:
Lorsque quatre états sont reliés par un modèle aussi précis, nous avons affaire à la configuration d'une «entité» dans notre modèle .
Une métaphore extrême est de savoir comment nous décririons un "être humain" dans un modèle, si nous n'étions pas conscients de leur existence en tant qu'entités unitaires avec des composants connectés à des degrés de liberté spécifiques: il faudrait décrire des états indépendants de "torses", «bras», «jambes» et «tête», ce qui compliquerait la compréhension du système décrit. Un résultat immédiat serait des expressions booléennes anormalement compliquées.
De toute évidence, le moyen de réduire la complexité est l'abstraction et un outil de choix en c ++ est le paradigme objet .
La question est donc la suivante: pourquoi existe-t-il un tel schéma? Qu'est-ce que c'est et que représente-t-il?
Comme on ne connaît pas la réponse, on peut se rabattre sur une abstraction mathématique: le tableau : on a trois scénarios, dont chacun est maintenant un tableau.
À quel point vous avez votre configuration initiale. comme un tableau. Par exemple,
std::array
a un opérateur d'égalité:À quel point votre syntaxe devient:
Tout comme la réponse de Gian Paolo, elle est courte, claire et facilement vérifiable / débuggable. Dans ce cas, nous avons délégué les détails des expressions booléennes au compilateur.
la source
Vous n'aurez pas à vous soucier des combinaisons invalides d'indicateurs booléens si vous vous débarrassez des indicateurs booléens.
Vous avez clairement trois états (scénarios). Il serait préférable de modéliser cela et de dériver les propriétés booléennes de ces états, et non l'inverse.
C'est certainement plus de code que dans la réponse de Gian Paolo , mais selon votre situation, cela pourrait être beaucoup plus maintenable:
enum
cas non gérés dans lesswitch
instructions interceptera les acquéreurs de propriétés qui ne gèrent pas ce scénario.Cette approche a également l'avantage d'être très efficace.
la source
Mes 2 cents: déclarez une somme variable (entier) pour que
Vérifiez la somme par rapport aux conditions que vous souhaitez et c'est tout. De cette façon, vous pouvez facilement ajouter plus de conditions à l'avenir en le maintenant assez simple à lire.
la source
La réponse acceptée est bonne lorsque vous n'avez que 3 cas et où la logique de chacun est simple.
Mais si la logique de chaque cas était plus compliquée, ou s'il y a beaucoup plus de cas, une bien meilleure option consiste à utiliser le modèle de conception de la chaîne de responsabilité .
Vous créez un
BaseValidator
qui contient une référence à aBaseValidator
et une méthode àvalidate
et une méthode pour appeler la validation sur le validateur référencé.Ensuite, vous créez un certain nombre de sous-classes qui héritent de
BaseValidator
, en remplaçant lavalidate
méthode par la logique nécessaire pour chaque validateur.Ensuite, son utilisation est simple, instanciez chacun de vos validateurs et définissez chacun d'entre eux comme étant la racine des autres:
En substance, chaque cas de validation a sa propre classe qui est responsable (a) de déterminer si la validation correspond à ce cas, et (b) d'envoyer la validation à quelqu'un d'autre dans la chaîne si ce n'est pas le cas.
Veuillez noter que je ne suis pas familier avec C ++. J'ai essayé de faire correspondre la syntaxe de certains exemples que j'ai trouvés en ligne, mais si cela ne fonctionne pas, traitez-le plus comme un pseudocode. J'ai également un exemple Python fonctionnel complet ci-dessous qui peut être utilisé comme base si vous le souhaitez.
Encore une fois, vous pouvez trouver cette surpuissance pour votre exemple spécifique, mais cela crée un code beaucoup plus propre si vous vous retrouvez avec un ensemble de cas beaucoup plus compliqués qui doivent être remplis.
la source
Une approche simple consiste à trouver la réponse que vous jugez acceptable.
Oui = (booléen1 && booléen2 && booléen3 && booléen4) + + ...
Maintenant, si possible, simplifiez l'équation en utilisant l'algèbre booléenne.
comme dans ce cas, acceptable1 et 2 se combinent en
(boolean1 && boolean2 && boolean3)
.Par conséquent, la réponse finale est:
la source
utiliser le champ de bits :
PS :
C'est vraiment dommage pour les CPP. Mais, UB n'est pas mon souci, vérifiez-le sur http://coliru.stacked-crooked.com/a/2b556abfc28574a1 .
la source
unsigned char*
, bien que je pense que le simple fait d'utiliser quelque chose comme ce((((flag4 <<1) | flag3) << 1) | flag2) << 1) | flag1
serait probablement plus efficace.