valores usar programacion operadores logicos ejemplos comparacion como booleanos bool c++ if-statement

c++ - usar - Cómo mejorar la lógica para verificar si 4 valores booleanos coinciden con algunos casos



operadores logicos javascript (30)

Tengo cuatro valores bool :

bool bValue1; bool bValue2; bool bValue3; bool bValue4;

Los valores aceptables son:

Scenario 1 | Scenario 2 | Scenario 3 bValue1: true | true | true bValue2: true | true | false bValue3: true | true | false bValue4: true | false | false

Entonces, por ejemplo, este escenario no es aceptable:

bValue1: false bValue2: true bValue3: true bValue4: true

Por el momento, he encontrado esta declaración if para detectar escenarios erróneos:

if(((bValue4 && (!bValue3 || !bValue2 || !bValue1)) || ((bValue3 && (!bValue2 || !bValue1)) || (bValue2 && !bValue1) || (!bValue1 && !bValue2 && !bValue3 && !bValue4)) { // There is some error }

¿Se puede mejorar / simplificar esa lógica de enunciado?


El if s anidado podría ser más fácil de leer para algunas personas. Aqui esta mi version

bool check(int bValue1, int bValue2, int bValue3, int bValue4) { if (bValue1) { if (bValue2) { // scenario 1-2 return bValue3; } else { // scenario 3 return !bValue3 && !bValue4; } } return false; }


Estoy denotando a, b, c, d para mayor claridad y A, B, C, D para complementos

bValue1 = a (!A) bValue2 = b (!B) bValue3 = c (!C) bValue4 = d (!D)

Ecuación

1 = abcd + abcD + aBCD = a (bcd + bcD + BCD) = a (bc + BCD) = a (bcd + D (b ^C))

Usa las ecuaciones que más te convengan.


Hacer la operación en modo bit se ve muy limpio y comprensible.

int bitwise = (bValue4 << 3) | (bValue3 << 2) | (bValue2 << 1) | (bValue1); if (bitwise == 0b1111 || bitwise == 0b0111 || bitwise == 0b0001) { //satisfying condition }


La respuesta aceptada está bien cuando solo tienes 3 casos, y donde la lógica para cada uno es simple.

Pero si la lógica de cada caso fuera más complicada, o si hubiera muchos más, una opción mucho mejor es usar el patrón de diseño de la chain-of-responsibility .

Usted crea una BaseValidator que contiene una referencia a un BaseValidator método validate y un método para llamar a la validación en el validador al que se hace referencia.

class BaseValidator { BaseValidator* nextValidator; public: BaseValidator() { nextValidator = 0; } void link(BaseValidator validator) { if (nextValidator) { nextValidator->link(validator); } else { nextValidator = validator; } } bool callLinkedValidator(bool v1, bool v2, bool v3, bool v4) { if (nextValidator) { return nextValidator->validate(v1, v2, v3, v4); } return false; } virtual bool validate(bool v1, bool v2, bool v3, bool v4) { return false; } }

A continuación, crea una serie de subclases que heredan de la BaseValidator , anulando el validate método con la lógica necesaria para cada validador.

class Validator1: public BaseValidator { public: bool validate(bool v1, bool v2, bool v3, bool v4) { if (v1 && v2 && v3 && v4) { return true; } return nextValidator->callLinkedValidator(v1, v2, v3, v4); } }

Luego, usarlo es sencillo, crear una instancia de cada uno de sus validadores y establecer que cada uno de ellos sea la raíz de los otros:

Validator1 firstValidator = new Validator1(); Validator2 secondValidator = new Validator2(); Validator3 thirdValidator = new Validator3(); firstValidator.link(secondValidator); firstValidator.link(thirdValidator); if (firstValidator.validate(value1, value2, value3, value4)) { ... }

En esencia, cada caso de validación tiene su propia clase, que es responsable de (a) determinar si la validación coincide con ese caso, y (b) enviar la validación a otra persona de la cadena si no es así.

Tenga en cuenta que no estoy familiarizado con C ++. Traté de hacer coincidir la sintaxis de algunos ejemplos que encontré en línea, pero si esto no funciona, trátelo más como pseudocódigo. También tengo un ejemplo completo de Python de trabajo que se puede usar como base, si así lo prefieres.

class BaseValidator: def __init__(self): self.nextValidator = 0 def link(self, validator): if (self.nextValidator): self.nextValidator.link(validator) else: self.nextValidator = validator def callLinkedValidator(self, v1, v2, v3, v4): if (self.nextValidator): return self.nextValidator.validate(v1, v2, v3, v4) return False def validate(self, v1, v2, v3, v4): return False class Validator1(BaseValidator): def validate(self, v1, v2, v3, v4): if (v1 and v2 and v3 and v4): return True return self.callLinkedValidator(v1, v2, v3, v4) class Validator2(BaseValidator): def validate(self, v1, v2, v3, v4): if (v1 and v2 and v3 and not v4): return True return self.callLinkedValidator(v1, v2, v3, v4) class Validator3(BaseValidator): def validate(self, v1, v2, v3, v4): if (v1 and not v2 and not v3 and not v4): return True return self.callLinkedValidator(v1, v2, v3, v4) firstValidator = Validator1() secondValidator = Validator2() thirdValidator = Validator3() firstValidator.link(secondValidator) firstValidator.link(thirdValidator) print(firstValidator.validate(False, False, True, False))

Una vez más, puede encontrar este exceso para su ejemplo específico, pero crea un código mucho más limpio si termina con un conjunto mucho más complicado de casos que deben cumplirse.


Mis 2 centavos: declare una suma variable (entero) para que

if(bValue1) { sum=sum+1; } if(bValue2) { sum=sum+2; } if(bValue3) { sum=sum+4; } if(bValue4) { sum=sum+8; }

Compruebe la suma contra las condiciones que desee y eso es todo. De esta manera puede agregar fácilmente más condiciones en el futuro, por lo que es bastante fácil de leer.


No tendrás que preocuparte por las combinaciones no válidas de banderas booleanas si te deshaces de ellas.

Los valores aceptables son:

Scenario 1 | Scenario 2 | Scenario 3 bValue1: true | true | true bValue2: true | true | false bValue3: true | true | false bValue4: true | false | false

Claramente tienes tres estados (escenarios). Sería mejor modelar eso y derivar las propiedades booleanas de esos estados, no al revés.

enum State { scenario1, scenario2, scenario3, }; inline bool isValue1(State s) { // (Well, this is kind of silly. Do you really need this flag?) return true; } inline bool isValue2(State s) { switch (s) { case scenario1: case scenario2: return true; case scenario3: return false; } } inline bool isValue3(State s) { // (This is silly too. Do you really need this flag?) return isValue2(s); } inline bool isValue4(State s) { switch (s) { case scenario1: return true; case scenario2: case scenario3: return false; } }

Este es definitivamente más código que en la respuesta de Gian Paolo , pero dependiendo de su situación, esto podría ser mucho más fácil de mantener:

  • Hay un conjunto central de funciones para modificar si se agregan propiedades o escenarios booleanos adicionales.
    • Agregar propiedades requiere agregar una sola función.
    • Si agrega un escenario, habilitar las advertencias del compilador sobre los enum casos switch no manejados en las declaraciones capturará a los que obtienen propiedades que no manejan ese escenario.
  • Si necesita modificar las propiedades booleanas dinámicamente, no necesita volver a validar sus combinaciones en todas partes. En lugar de alternar las banderas booleanas individuales (que podrían resultar en combinaciones de banderas no válidas), en cambio tendría una máquina de estado que hace la transición de un escenario a otro.

Este enfoque también tiene el beneficio secundario de ser muy eficiente.


Primero, asumiendo que solo puede modificar la verificación de escenario, me concentraría en la legibilidad y simplemente ajustaría la verificación en una función para que solo pueda llamar if(ScenarioA()) .

Ahora, suponiendo que realmente desee / necesite optimizar esto, recomendaría convertir los booleanos estrechamente vinculados en enteros constantes, y utilizar operadores de bits en ellos

public class Options { public const bool A = 2; // 0001 public const bool B = 4; // 0010 public const bool C = 16;// 0100 public const bool D = 32;// 1000 //public const bool N = 2^n; (up to n=32) } ... public isScenario3(int options) { int s3 = Options.A | Options.B | Options.C; // for true if only s3 options are set return options == s3; // for true if s3 options are set // return options & s3 == s3 }

Esto hace que expresar los escenarios sea tan fácil como enumerar lo que forma parte de él, le permite usar una instrucción de cambio para saltar a la condición correcta y confundir a otros desarrolladores que no han visto esto antes. (C # RegexOptions usa este patrón para establecer indicadores, no sé si hay un ejemplo de biblioteca de c ++)


Se han dado varias respuestas correctas a esta pregunta, pero yo adoptaría una perspectiva diferente: si el código parece demasiado complicado, algo no está bien . El código será difícil de depurar y es más probable que sea "de un solo uso".

En la vida real, cuando encontramos una situación como esta:

Scenario 1 | Scenario 2 | Scenario 3 bValue1: true | true | true bValue2: true | true | false bValue3: true | true | false bValue4: true | false | false

Cuando cuatro estados están conectados por un patrón tan preciso, estamos tratando con la configuración de alguna "entidad" en nuestro modelo .

Una metáfora extrema es cómo describiríamos a un "ser humano" en un modelo, si no fuéramos conscientes de su existencia como entidades unitarias con componentes conectados a grados específicos de libertad: tendríamos que describir estados independientes de "torso", "brazos", "piernas" y "cabeza", lo que complicaría el sentido del sistema descrito. Un resultado inmediato sería expresiones booleanas anormalmente complicadas.

Obviamente, la forma de reducir la complejidad es la abstracción y una herramienta de elección en c ++ es el paradigma del objeto .

Entonces la pregunta es: ¿por qué existe tal patrón? ¿Qué es esto y qué representa?

Como no sabemos la respuesta, podemos recurrir a una abstracción matemática: la matriz : tenemos tres escenarios, cada uno de los cuales es ahora una matriz.

0 1 2 3 Scenario 1: T T T T Scenario 2: T T T F Scenario 3: T F F F

En qué punto tienes tu configuración inicial. como una matriz. Por ejemplo, std::array tiene un operador de igualdad:

En qué punto su sintaxis se convierte en:

if( myarray == scenario1 ) { // arrays contents are the same } else if ( myarray == scenario2 ) { // arrays contents are the same } else if ( myarray == scenario3 ) { // arrays contents are the same } else { // not the same }

Al igual que la respuesta de Gian Paolo, es breve, clara y fácilmente verificable / debugable. En este caso, hemos delegado los detalles de las expresiones booleanas al compilador.


Solo una preferencia personal sobre la respuesta aceptada, pero escribiría:

bool valid = false; // scenario 1 valid = valid || (bValue1 && bValue2 && bValue3 && bValue4); // scenario 2 valid = valid || (bValue1 && bValue2 && bValue3 && !bValue4); // scenario 3 valid = valid || (bValue1 && !bValue2 && !bValue3 && !bValue4);


Un enfoque simple es encontrar la respuesta que considere aceptable.

Sí = (boolean1 && boolean2 && boolean3 && boolean4) + + ...

Ahora, si es posible, simplifica la ecuación usando álgebra booleana.

Como en este caso, aceptable1 y 2 se combinan para (boolean1 && boolean2 && boolean3) .

De ahí que la respuesta final sea:

(boolean1 && boolean2 && boolean3) || ((boolean1 && !boolean2 && !boolean3 && !boolean4)


usar campo de bits :

unoin { struct { bool b1: 1; bool b2: 1; bool b3: 1; bool b4: 1; } b; int i; } u; // set: u.b.b1=true; ... // test if (u.i == 0x0f) {...} if (u.i == 0x0e) {...} if (u.i == 0x08) {...}

PS :

Eso es una gran pena para los CPPers ''. Pero, UB no es mi preocupación, verifíquelo en http://coliru.stacked-crooked.com/a/2b556abfc28574a1 .


Apuntaría a la legibilidad: usted tiene solo 3 escenarios, trátelos con 3 ifs separados:

bool valid = false; if (bValue1 && bValue2 && bValue3 && bValue4) valid = true; //scenario 1 else if (bValue1 && bValue2 && bValue3 && !bValue4) valid = true; //scenario 2 else if (bValue1 && !bValue2 && !bValue3 && !bValue4) valid = true; //scenario 3

Fácil de leer y depurar, IMHO. Además, puede asignar una variable whichScenario mientras whichScenario con el if .

Con solo 3 escenarios, no me atrevería a decir algo así "si los primeros 3 valores son ciertos, puedo evitar verificar el cuarto valor": hará que su código sea más difícil de leer y mantener.

No es una solución elegante tal vez Seguramente, pero en este caso está bien: fácil y legible.

Si su lógica se vuelve más complicada, deseche ese código y considere usar algo más para almacenar diferentes escenarios disponibles (como sugiere Zladeck).

Realmente me encanta la primera sugerencia dada en esta respuesta : fácil de leer, no propenso a errores, mantenible

(Casi) fuera de tema:

No escribo muchas respuestas aquí en . Es realmente gracioso que la respuesta aceptada anteriormente sea, con mucho, la respuesta más apreciada en mi historia (nunca tuve más de 5 a 10 votos antes de lo que pienso), aunque en realidad no es lo que generalmente creo que es la forma "correcta" de hacerlo.

Pero la simplicidad a menudo es "la forma correcta de hacerlo", muchas personas parecen pensar esto y yo debería pensar que es más que yo :)


Apuntaría a la simplicidad y la legibilidad.

bool scenario1 = bValue1 && bValue2 && bValue3 && bValue4; bool scenario2 = bValue1 && bValue2 && bValue3 && !bValue4; bool scenario3 = bValue1 && !bValue2 && !bValue3 && !bValue4; if (scenario1 || scenario2 || scenario3) { // Do whatever. }

Asegúrese de reemplazar los nombres de los escenarios, así como los nombres de las banderas con algo descriptivo. Si tiene sentido para su problema específico, podría considerar esta alternativa:

bool scenario1or2 = bValue1 && bValue2 && bValue3; bool scenario3 = bValue1 && !bValue2 && !bValue3 && !bValue4; if (scenario1or2 || scenario3) { // Do whatever. }

Lo que es importante aquí no es la lógica del predicado. Es describir su dominio y expresar claramente su intención. La clave aquí es dar a todas las entradas y variables intermedias buenos nombres. Si no puede encontrar buenos nombres de variables, puede ser una señal de que está describiendo el problema de manera incorrecta.


Aquí hay una versión simplificada:

if (bValue1&&(bValue2==bValue3)&&(bValue2||!bValue4)) { // acceptable } else { // not acceptable }

Tenga en cuenta que, por supuesto, esta solución es más confusa que la original, su significado puede ser más difícil de entender.

Actualización: MSalters en los comentarios encontró una expresión aún más simple:

if (bValue1&&(bValue2==bValue3)&&(bValue2>=bValue4)) ...


Cada respuesta es demasiado compleja y difícil de leer. La mejor solución para esto es una sentencia switch() . Es legible y simplifica la adición / modificación de casos adicionales. Los compiladores son buenos para optimizar sentencias switch() también.

switch( (bValue4 << 3) | (bValue3 << 2) | (bValue2 << 1) | (bValue1) ) { case 0b1111: // scenario 1 break; case 0b0111: // scenario 2 break; case 0b0001: // scenario 3 break; default: // fault condition break; }

Por supuesto, puede usar constantes y OR juntas en las declaraciones del case para una mayor legibilidad.


Considere traducir sus tablas lo más directamente posible a su programa. Conduzca el programa basado fuera de la tabla, en lugar de imitarlo con lógica.

template<class T0> auto is_any_of( T0 const& t0, std::initializer_list<T0> il ) { for (auto&& x:il) if (x==t0) return true; return false; }

ahora

if (is_any_of( std::make_tuple(bValue1, bValue2, bValue3, bValue4), { {true, true, true, true}, {true, true, true, false}, {true, false, false, false} } ))

esto directamente como posible codifica su tabla de verdad en el compilador.

Ejemplo vivo .

También puedes usar std::any_of directamente:

using entry = std::array<bool, 4>; constexpr entry acceptable[] = { {true, true, true, true}, {true, true, true, false}, {true, false, false, false} }; if (std::any_of( begin(acceptable), end(acceptable), [&](auto&&x){ return entry{bValue1, bValue2, bValue3, bValue4} == x; }) { }

el compilador puede integrar el código, eliminar cualquier iteración y construir su propia lógica para usted. Mientras tanto, su código refleja exactamente cómo se enteró del problema.


Es fácil notar que los dos primeros escenarios son similares: comparten la mayoría de las condiciones. Si desea seleccionar el escenario en el que se encuentra en este momento, puede escribirlo así (es una solución modificada de @gian-paolo ):

bool valid = false; if(bValue1 && bValue2 && bValue3) { if (bValue4) valid = true; //scenario 1 else if (!bValue4) valid = true; //scenario 2 } else if (bValue1 && !bValue2 && !bValue3 && !bValue4) valid = true; //scenario 3

Yendo más allá, puedes notar que el primer booleano debe ser siempre verdadero, lo cual es una condición de entrada, por lo que puedes terminar con:

bool valid = false; if(bValue1) { if(bValue2 && bValue3) { if (bValue4) valid = true; //scenario 1 else if (!bValue4) valid = true; //scenario 2 } else if (!bValue2 && !bValue3 && !bValue4) valid = true; //scenario 3 }

Aún más, ahora puede ver claramente que bValue2 y bValue3 están un tanto conectados; podría extraer su estado a algunas funciones o variables externas con un nombre más apropiado (aunque esto no siempre es fácil o apropiado):

bool valid = false; if(bValue1) { bool bValue1and2 = bValue1 && bValue2; bool notBValue1and2 = !bValue2 && !bValue3; if(bValue1and2) { if (bValue4) valid = true; //scenario 1 else if (!bValue4) valid = true; //scenario 2 } else if (notBValue1and2 && !bValue4) valid = true; //scenario 3 }

Hacerlo de esta manera tiene algunas ventajas y desventajas:

  • Las condiciones son más pequeñas, por lo que es más fácil razonar sobre ellas.
  • es más fácil hacer un buen cambio de nombre para hacer que estas condiciones sean más comprensibles,
  • pero, requieren entender el alcance,
  • además es más rígido

Si predice que habrá cambios en la lógica anterior, debe usar un enfoque más directo como lo presenta @gian-paolo .

De lo contrario, si estas condiciones están bien establecidas y son una especie de "reglas sólidas" que nunca cambiarán, considere mi último fragmento de código.


La pregunta real aquí es: qué sucede cuando otro desarrollador (o incluso un autor) debe cambiar este código unos meses más tarde.

Sugeriría modelar esto como banderas de bits:

const int SCENARIO_1 = 0x0F; // 0b1111 if using c++14 const int SCENARIO_2 = 0x0E; // 0b1110 const int SCENARIO_3 = 0x08; // 0b1000 bool bValue1 = true; bool bValue2 = false; bool bValue3 = false; bool bValue4 = false; // boolean -> int conversion is covered by standard and produces 0/1 int scenario = bValue1 << 3 | bValue2 << 2 | bValue3 << 1 | bValue4; bool match = scenario == SCENARIO_1 || scenario == SCENARIO_2 || scenario == SCENARIO_3; std::cout << (match ? "ok" : "error");

Si hay muchos más escenarios o más banderas, un enfoque de tabla es más legible y extensible que el uso de banderas. Para soportar un nuevo escenario se requiere solo otra fila en la tabla.

int scenarios[3][4] = { {true, true, true, true}, {true, true, true, false}, {true, false, false, false}, }; int main() { bool bValue1 = true; bool bValue2 = false; bool bValue3 = true; bool bValue4 = true; bool match = false; // depending on compiler, prefer std::size()/_countof instead of magic value of 4 for (int i = 0; i < 4 && !match; ++i) { auto current = scenarios[i]; match = bValue1 == current[0] && bValue2 == current[1] && bValue3 == current[2] && bValue4 == current[3]; } std::cout << (match ? "ok" : "error"); }


Mi respuesta anterior ya es la respuesta aceptada. Agrego algo aquí que creo que es fácil de leer, fácil y, en este caso, abierto a futuras modificaciones:

Comenzando con la respuesta de @ZdeslavVojkovic (que me parece bastante buena), se me ocurrió esto:

#include <iostream> #include <set> //using namespace std; int GetScenarioInt(bool bValue1, bool bValue2, bool bValue3, bool bValue4) { return bValue1 << 3 | bValue2 << 2 | bValue3 << 1 | bValue4; } bool IsValidScenario(bool bValue1, bool bValue2, bool bValue3, bool bValue4) { std::set<int> validScenarios; validScenarios.insert(GetScenarioInt(true, true, true, true)); validScenarios.insert(GetScenarioInt(true, true, true, false)); validScenarios.insert(GetScenarioInt(true, false, false, false)); int currentScenario = GetScenarioInt(bValue1, bValue2, bValue3, bValue4); return validScenarios.find(currentScenario) != validScenarios.end(); } int main() { std::cout << IsValidScenario(true, true, true, false) << "/n"; // expected = true; std::cout << IsValidScenario(true, true, false, false) << "/n"; // expected = false; return 0; }

Véalo en el trabajo here

Bueno, esa es la solución "elegante y mantenible" (IMHO) a la que generalmente apunto, pero en realidad, para el caso OP, mi respuesta anterior "montón de ifs" se ajusta mejor a los requisitos OP, incluso si no es elegante ni mantenible.


No veo ninguna respuesta que diga nombrar los escenarios, aunque la solución del OP hace exactamente eso.

Para mí, es mejor encapsular el comentario de lo que es cada escenario en un nombre de variable o un nombre de función. Es más probable que ignore un comentario que un nombre, y si su lógica cambia en el futuro, es más probable que cambie un nombre que un comentario. No puedes refactorizar un comentario.

Si planea reutilizar estos escenarios fuera de su función (o tal vez quiera), haga una función que diga lo que evalúa ( constexpr / noexcept opcional pero recomendado):

constexpr bool IsScenario1(bool b1, bool b2, bool b3, bool b4) noexcept { return b1 && b2 && b3 && b4; } constexpr bool IsScenario2(bool b1, bool b2, bool b3, bool b4) noexcept { return b1 && b2 && b3 && !b4; } constexpr bool IsScenario3(bool b1, bool b2, bool b3, bool b4) noexcept { return b1 && !b2 && !b3 && !b4; }

Haga estos métodos de clase si es posible (como en la solución de OP). Puede usar variables dentro de su función si no cree que reutilizará la lógica:

const auto is_scenario_1 = bValue1 && bValue2 && bValue3 && bValue4; const auto is_scenario_2 = bvalue1 && bvalue2 && bValue3 && !bValue4; const auto is_scenario_3 = bValue1 && !bValue2 && !bValue3 && !bValue4;

Lo más probable es que el compilador resuelva que si bValue1 es falso, todos los escenarios son falsos. No te preocupes por hacerlo rápido, simplemente correcto y legible. Si crea un perfil de su código y considera que se trata de un cuello de botella debido a que el compilador generó un código subóptimo en -O2 o superior, intente volver a escribirlo.


Podemos usar un en.wikipedia.org/wiki/Karnaugh_map y reducir sus escenarios a una ecuación lógica. He utilizado el solucionador de mapas Karnaugh en línea con circuito para 4 variables.

Esto produce:

Cambiando A, B, C, D a bValue1, bValue2, bValue3, bValue4 , esto no es más que:

bValue1 && bValue2 && bValue3 || bValue1 && !bValue2 && !bValue3 && !bValue4

Entonces tu sentencia if convierte en:

if(!(bValue1 && bValue2 && bValue3 || bValue1 && !bValue2 && !bValue3 && !bValue4)) { // There is some error }

  • Los mapas de Karnaugh son particularmente útiles cuando tienes muchas variables y muchas condiciones que deberían ser verificadas.
  • Después de reducir los escenarios true a una ecuación lógica, agregar comentarios relevantes que indiquen los escenarios true es una buena práctica.

Según lo sugerido por mch, usted podría hacer:

if(!((bValue1 && bValue2 && bValue3) || (bValue1 && !bValue2 && !bValue3 && !bValue4)) )

donde la primera línea cubre los dos primeros casos buenos, y la segunda línea cubre el último.

Demostración en vivo , donde jugué un poco y pasa tus casos.


Solo proporciono mi respuesta aquí, como en los comentarios que alguien sugirió para mostrar mi solución. Quiero agradecer a todos por sus ideas.

Al final opté por agregar tres nuevos métodos boolean "escenario":

bool CChristianLifeMinistryValidationDlg::IsFirstWeekStudentItems(CChristianLifeMinistryEntry *pEntry) { return (INCLUDE_ITEM1(pEntry) && !INCLUDE_ITEM2(pEntry) && !INCLUDE_ITEM3(pEntry) && !INCLUDE_ITEM4(pEntry)); } bool CChristianLifeMinistryValidationDlg::IsSecondWeekStudentItems(CChristianLifeMinistryEntry *pEntry) { return (INCLUDE_ITEM1(pEntry) && INCLUDE_ITEM2(pEntry) && INCLUDE_ITEM3(pEntry) && INCLUDE_ITEM4(pEntry)); } bool CChristianLifeMinistryValidationDlg::IsOtherWeekStudentItems(CChristianLifeMinistryEntry *pEntry) { return (INCLUDE_ITEM1(pEntry) && INCLUDE_ITEM2(pEntry) && INCLUDE_ITEM3(pEntry) && !INCLUDE_ITEM4(pEntry)); }

Entonces pude aplicar esas mi rutina de validación de esta manera:

if (!IsFirstWeekStudentItems(pEntry) && !IsSecondWeekStudentItems(pEntry) && !IsOtherWeekStudentItems(pEntry)) { ; Error }

En mi aplicación en vivo, los 4 valores bool en realidad se extraen de un DWORD que tiene 4 valores codificados en él.

Gracias de nuevo a todos.


También me gustaría presentar otro enfoque.

Mi idea es convertir los bools en un entero y luego compararlos utilizando varias plantillas:

unsigned bitmap_from_bools(bool b) { return b; } template<typename... args> unsigned bitmap_from_bools(bool b, args... pack) { return (bitmap_from_bools(b) << sizeof...(pack)) | bitmap_from_bools(pack...); } int main() { bool bValue1; bool bValue2; bool bValue3; bool bValue4; unsigned summary = bitmap_from_bools(bValue1, bValue2, bValue3, bValue4); if (summary != 0b1111u && summary != 0b1110u && summary != 0b1000u) { //bad scenario } }

Observe cómo este sistema puede admitir hasta 32 bools como entrada. Reemplazar lo unsigned con unsigned long long (o uint64_t ) aumenta el soporte a 64 casos. Si no te gusta el if (summary != 0b1111u && summary != 0b1110u && summary != 0b1000u) , también puede utilizar otro método de plantilla variadic:

bool equals_any(unsigned target, unsigned compare) { return target == compare; } template<typename... args> bool equals_any(unsigned target, unsigned compare, args... compare_pack) { return equals_any(target, compare) ? true : equals_any(target, compare_pack...); } int main() { bool bValue1; bool bValue2; bool bValue3; bool bValue4; unsigned summary = bitmap_from_bools(bValue1, bValue2, bValue3, bValue4); if (!equals_any(summary, 0b1111u, 0b1110u, 0b1000u)) { //bad scenario } }


También usaría variables de acceso directo para mayor claridad. Como se señaló anteriormente, el escenario 1 es igual al escenario 2, porque el valor de bValue4 no influye en la verdad de esos dos escenarios.

bool MAJORLY_TRUE=bValue1 && bValue2 && bValue3 bool MAJORLY_FALSE=!(bValue2 || bValue3 || bValue4)

entonces tu expresión beomes:

if (MAJORLY_TRUE || (bValue1 && MAJORLY_FALSE)) { // do something } else { // There is some error }

Dar nombres significativos a las variables MAJORTRUE y MAJORFALSE (así como también a bValue * vars) ayudaría mucho con la legibilidad y el mantenimiento.


Una pequeña variación en la fina respuesta de @ GianPaolo, que algunos pueden encontrar más fácil de leer:

bool any_of_three_scenarios(bool v1, bool v2, bool v3, bool v4) { return (v1 && v2 && v3 && v4) // scenario 1 || (v1 && v2 && v3 && !v4) // scenario 2 || (v1 && !v2 && !v3 && !v4); // scenario 3 } if (any_of_three_scenarios(bValue1,bValue2,bValue3,bValue4)) { // ... }


Vía AC / C ++

bool scenario[3][4] = {{true, true, true, true}, {true, true, true, false}, {true, false, false, false}}; bool CheckScenario(bool bValue1, bool bValue2, bool bValue3, bool bValue4) { bool temp[] = {bValue1, bValue2, bValue3, bValue4}; for(int i = 0 ; i < sizeof(scenario) / sizeof(scenario[0]); i++) { if(memcmp(temp, scenario[i], sizeof(temp)) == 0) return true; } return false; }

Este enfoque es escalable, ya que si la cantidad de condiciones válidas aumenta, simplemente agrega más de ellas a la lista de escenarios.


Depende de lo que representen.

Por ejemplo, si 1 es una clave, y 2 y 3 son dos personas que deben estar de acuerdo (excepto si están de acuerdo en NOT que necesitan una tercera persona, 4 para confirmar), lo más legible podría ser:

1 && ( (2 && 3) || ((!2 && !3) && !4) )

por solicitud popular:

Key && ( (Alice && Bob) || ((!Alice && !Bob) && !Charlie) )


Concéntrese en la legibilidad del problema, no en la declaración "if" específica.

Si bien esto producirá más líneas de código, y algunos pueden considerarlo excesivo o innecesario. Yo sugeriría que abstener tus escenarios de los booleanos específicos es la mejor manera de mantener la legibilidad.

Al dividir las cosas en clases (no dude en usar funciones, o cualquier otra herramienta que prefiera) con nombres comprensibles, podemos mostrar mucho más fácilmente los significados detrás de cada escenario. Más importante aún, en un sistema con muchas partes móviles, es más fácil de mantener y unirse a sus sistemas existentes (nuevamente, a pesar de la cantidad de código adicional que implica).

#include <iostream> #include <vector> using namespace std; // These values would likely not come from a single struct in real life // Instead, they may be references to other booleans in other systems struct Values { bool bValue1; // These would be given better names in reality bool bValue2; // e.g. bDidTheCarCatchFire bool bValue3; // and bDidTheWindshieldFallOff bool bValue4; }; class Scenario { public: Scenario(Values& values) : mValues(values) {} virtual operator bool() = 0; protected: Values& mValues; }; // Names as examples of things that describe your "scenarios" more effectively class Scenario1_TheCarWasNotDamagedAtAll : public Scenario { public: Scenario1_TheCarWasNotDamagedAtAll(Values& values) : Scenario(values) {} virtual operator bool() { return mValues.bValue1 && mValues.bValue2 && mValues.bValue3 && mValues.bValue4; } }; class Scenario2_TheCarBreaksDownButDidntGoOnFire : public Scenario { public: Scenario2_TheCarBreaksDownButDidntGoOnFire(Values& values) : Scenario(values) {} virtual operator bool() { return mValues.bValue1 && mValues.bValue2 && mValues.bValue3 && !mValues.bValue4; } }; class Scenario3_TheCarWasCompletelyWreckedAndFireEverywhere : public Scenario { public: Scenario3_TheCarWasCompletelyWreckedAndFireEverywhere(Values& values) : Scenario(values) {} virtual operator bool() { return mValues.bValue1 && !mValues.bValue2 && !mValues.bValue3 && !mValues.bValue4; } }; Scenario* findMatchingScenario(std::vector<Scenario*>& scenarios) { for(std::vector<Scenario*>::iterator it = scenarios.begin(); it != scenarios.end(); it++) { if (**it) { return *it; } } return NULL; } int main() { Values values = {true, true, true, true}; std::vector<Scenario*> scenarios = { new Scenario1_TheCarWasNotDamagedAtAll(values), new Scenario2_TheCarBreaksDownButDidntGoOnFire(values), new Scenario3_TheCarWasCompletelyWreckedAndFireEverywhere(values) }; Scenario* matchingScenario = findMatchingScenario(scenarios); if(matchingScenario) { std::cout << matchingScenario << " was a match" << std::endl; } else { std::cout << "No match" << std::endl; } // your code goes here return 0; }


If (!bValue1 || (bValue2 != bValue3) || (!bValue4 && bValue2)) { // you have a problem }

  • b1 siempre debe ser verdad
  • b2 siempre debe ser igual a b3
  • y b4 no puede ser falso si b2 (y b3) son verdaderos

sencillo