Tengo cuatro bool
valores:
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
De momento se me ha ocurrido esta if
declaración para detectar malos escenarios:
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 declaración?
c++
if-statement
Andrew Truckle
fuente
fuente
if
declaración compleja . Además, como se trata de indicadores booleanos, puede modelar cada escenario como una constante y compararlo.if (!((bValue1 && bValue2 && bValue3) || (bValue1 && !bValue2 && !bValue3 && !bValue4)))
bool scenario1 = bValue1 && bValue2 && bValue3 && bValue4;
Respuestas:
Mi objetivo es la legibilidad: solo tiene 3 escenarios, trátelos con 3 if separados:
Fácil de leer y depurar, en mi humilde opinión. Además, puede asignar una variable
whichScenario
mientras continúa con elif
.Con solo 3 escenarios, no iría con algo como "si los primeros 3 valores son verdaderos, puedo evitar comprobar el cuarto valor": hará que su código sea más difícil de leer y mantener.
No es una solución elegante
tal vezseguramente, 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 del tema:
No escribo muchas respuestas aquí en StackOverflow. Es realmente gracioso que la respuesta aceptada anteriormente sea, con mucho, la respuesta más apreciada en mi historia (creo que nunca tuve más de 5-10 votos a favor) mientras que en realidad no es lo que normalmente creo que es la forma "correcta" de hacerlo.
Pero la simplicidad es a menudo "la forma correcta de hacerlo", mucha gente parece pensar esto y yo debería pensarlo más que yo :)
fuente
valid
y separándolas con||
, en lugar de mutarvalid
dentro de bloques de instrucciones separados. No puedo poner un ejemplo en el comentario, pero puede alinear verticalmente los||
operadores a la izquierda para dejar esto muy claro; las condiciones individuales ya están entre paréntesis tanto como sea necesario (paraif
) por lo que no es necesario agregar ningún carácter a las expresiones más allá de lo que ya está allí.if($bValue1)
ya que siempre tiene que ser cierto, lo que técnicamente permite una pequeña mejora en el rendimiento (aunque aquí estamos hablando de cantidades insignificantes).bValue4
Mi objetivo es la simplicidad y la legibilidad.
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:
Lo importante aquí no es la lógica de predicados. Describe tu dominio y expresa claramente tu intención. La clave aquí es dar un buen nombre a todas las entradas y variables intermedias. Si no puede encontrar buenos nombres de variables, puede ser una señal de que está describiendo el problema de manera incorrecta.
fuente
Podemos usar un mapa de Karnaugh y reducir sus escenarios a una ecuación lógica. He utilizado el solucionador de mapas de Karnaugh en línea con circuito para 4 variables.
Esto produce:
Cambiando
A, B, C, D
abValue1, bValue2, bValue3, bValue4
, esto no es más que:Entonces tu
if
declaración se convierte en:true
.true
escenarios a una ecuación lógica, agregar comentarios relevantes que indiquen lostrue
escenarios es una buena práctica.fuente
//!(ABC + AB'C'D') (By K-Map logic)
. Ese sería un buen momento para que el desarrollador aprenda K-Maps si aún no los conoce.E
yF
condiciones y 4 nuevos escenarios? ¿Cuánto tiempo se tarda en actualizar estaif
declaración correctamente? ¿Cómo comprueba la revisión de código si está bien o no? El problema no está en el aspecto técnico, sino en el aspecto "comercial".A
:ABC + AB'C'D' = A(BC + B'C'D')
(esto se puede incluso factorizar,A(B ^ C)'(C + D')
aunque tendría cuidado al llamar a esto 'simplificación').La verdadera pregunta aquí es: ¿qué sucede cuando otro desarrollador (o incluso autor) debe cambiar este código unos meses después?
Sugeriría modelar esto como banderas de bits:
Si hay muchos más escenarios o más indicadores, un enfoque de tabla es más legible y extensible que usar indicadores. Apoyar un nuevo escenario requiere simplemente otra fila en la tabla.
fuente
SCENARIO_2 = true << 3 | true << 2 | true << 1 | false;
2: evitar las variables SCENARIO_X y luego almacenar todos los escenarios disponibles en a<std::set<int>
. Agregar un escenario va a ser algo comomySet.insert( true << 3 | false << 2 | true << 1 | false;
quizás un poco exagerado para solo 3 escenarios, OP aceptó la solución rápida, sucia y fácil que sugerí en mi respuesta.std::find
?).scenario
valor me parecen innecesariamente propensas a errores.Mi respuesta anterior ya es la respuesta aceptada, agrego algo aquí que creo que es legible, fácil y en este caso abierto a modificaciones futuras:
Comenzando con la respuesta de @ZdeslavVojkovic (que me parece bastante buena), se me ocurrió esto:
Véalo en el trabajo aquí
Bueno, esa es la solución "elegante y mantenible" (en mi humilde opinión) a la que suelo aspirar, pero en realidad, para el caso OP, mi anterior respuesta "montón de ifs" se ajusta mejor a los requisitos OP, incluso si no es elegante ni fácil de mantener.
fuente
También me gustaría presentar otro enfoque.
Mi idea es convertir los bools en un número entero y luego comparar usando plantillas variadic:
Observe cómo este sistema puede admitir hasta 32 bools como entrada. reemplazar el
unsigned
conunsigned long long
(ouint64_t
) aumenta el soporte a 64 casos. Si no le gustaif (summary != 0b1111u && summary != 0b1110u && summary != 0b1000u)
, también puede usar otro método de plantilla variadic:fuente
bitmap_from_bools
, obools_to_bitmap
?bools_to_unsigned
. Mapa de bits es una buena palabra clave; editado.summary!= 0b1111u &&...
.a != b || a != c
siempre es cierto sib != c
Aquí tienes una versión simplificada:
Tenga en cuenta, por supuesto, que esta solución está 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:
fuente
simple
es de hecho un término vago. Mucha gente lo entiende en este contexto como más simple de entender para el desarrollador y no para que el compilador genere código, por lo que más detallado puede ser más simple.Considere traducir sus tablas lo más directamente posible a su programa. Elimine el programa basado en la mesa, en lugar de imitarlo con lógica.
ahora
esto directamente codifica su tabla de verdad en el compilador.
Ejemplo vivo .
También puede usar
std::any_of
directamente:el compilador puede incorporar el código, eliminar cualquier iteración y construir su propia lógica para usted. Mientras tanto, su código refleja exactamente cómo concibió el problema.
fuente
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
boolean
métodos de "escenario" :Luego pude aplicar esas mi rutina de validación de esta manera:
En mi aplicación en vivo, los 4 valores bool se extraen de un
DWORD
que tiene 4 valores codificados.Gracias de nuevo a todos.
fuente
INCLUDE_ITEM1
etc. de una mejor manera y estás bien. :)No veo ninguna respuesta que diga que nombre 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 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 planeas reutilizar estos escenarios fuera de tu función (o tal vez quieras), crea una función que diga lo que evalúa (
constexpr
/noexcept
opcional pero recomendado):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:
Lo más probable es que el compilador resuelva que si bValue1 es falso, todos los escenarios son falsos. No se preocupe por hacerlo rápido, simplemente correcto y legible. Si perfila su código y encuentra que esto es un cuello de botella porque el compilador generó un código subóptimo en -O2 o superior, intente reescribirlo.
fuente
Manera AC / C ++
Este enfoque es escalable, ya que si aumentara el número de condiciones válidas, fácilmente se agregan más a la lista de escenarios.
fuente
true
. Un compilador que usa "cualquier cosa distinta de cero es verdadera" hace que este código falle. Tenga en cuenta quetrue
debe convertirse a1
, simplemente no necesita almacenarse como tal.2 is not equal to true but evaluates to true
, mi código no fuerzaint 1 = true
y funciona siempre que todos los verdaderos se conviertan al mismo valor int, así que aquí está mi pregunta: ¿Por qué el compilador debería actuar al azar al convertir fiel al int subyacente, ¿podría dar más detalles?memcmp
prueba de condiciones booleanas no es la forma de C ++, y dudo que sea una forma de C establecida.Es fácil notar que los dos primeros escenarios son similares: comparten la mayoría de las condiciones. Si desea seleccionar en qué escenario se encuentra en este momento, puede escribirlo así (es una solución de @ gian-paolo modificada ):
Yendo más allá, puede notar que el primer booleano debe ser siempre verdadero, que es una condición de entrada, por lo que puede terminar con:
Aún más, ahora puede ver claramente que bValue2 y bValue3 están algo conectados; podría extraer su estado a algunas funciones externas o variables con un nombre más apropiado (aunque esto no siempre es fácil o apropiado):
Hacerlo de esta manera tiene algunas ventajas y desventajas:
Si predice que habrá cambios en la lógica anterior, debe usar un enfoque más sencillo 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.
fuente
Como sugirió mch, podría hacer:
donde la primera línea cubre los dos primeros casos buenos y la segunda línea cubre el último.
Live Demo, donde jugué y pasa tus casos.
fuente
Una ligera variación en la excelente respuesta de @ GianPaolo, que algunos pueden encontrar más fácil de leer:
fuente
Cada respuesta es demasiado compleja y difícil de leer. La mejor solución a esto es una
switch()
declaración. Es legible y simplifica la adición / modificación de casos adicionales. Los compiladores también son buenos para optimizarswitch()
declaraciones.Por supuesto, puede usar constantes y OR juntas en las
case
declaraciones para una mayor legibilidad.fuente
También usaría variables de atajo 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.
entonces tu expresión se convierte en:
Dar nombres significativos a las variables MAJORTRUE y MAJORFALSE (así como en realidad a bValue * vars) ayudaría mucho con la legibilidad y el mantenimiento.
fuente
Concéntrese en la legibilidad del problema, no en la declaración específica "si".
Si bien esto producirá más líneas de código, algunos pueden considerarlo excesivo o innecesario. Sugeriría que abstraer sus escenarios de los valores booleanos específicos es la mejor manera de mantener la legibilidad.
Al dividir las cosas en clases (siéntase libre de 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 mantener y unir sus sistemas existentes (nuevamente, a pesar de la cantidad de código adicional que se invoque).
fuente
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), la más legible podría ser:por solicitud popular:
fuente
bValue
.La operación bit a bit se ve muy limpia y comprensible.
fuente
Estoy denotando a, b, c, d para mayor claridad y A, B, C, D para complementos.
Ecuación
Utilice cualquier ecuación que le convenga.
fuente
sencillo
fuente
Solo una preferencia personal sobre la respuesta aceptada, pero escribiría:
fuente
Primero, asumiendo que solo puede modificar la verificación del escenario, me centraría en la legibilidad y simplemente envolvería la verificación en una función para que pueda simplemente llamar
if(ScenarioA())
.Ahora, asumiendo que realmente desea / necesita optimizar esto, recomendaría convertir los booleanos estrechamente vinculados en enteros constantes y usar operadores de bits en ellos
Esto hace que expresar los escenarios sea tan fácil como enumerar lo que es parte de ellos, le permite usar una declaració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 configurar banderas, no sé si hay un ejemplo de biblioteca de C ++)
fuente
Los mensajes de correo electrónico anidados
if
podrían ser más fáciles de leer para algunas personas. Aqui esta mi versionfuente
bValue1
bloque, puede tratar todo lo que contiene como una nueva página nueva en su proceso mental. Apuesto a que la forma de abordar el problema puede ser muy personal o incluso cultural.Se han dado varias respuestas correctas a esta pregunta, pero yo adoptaría un punto de vista diferente: si el código parece demasiado complicado, algo no está del todo 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 nos encontramos con una situación como esta:
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 en grados específicos de libertad: tendríamos que describir estados independientes de "torsoes", "brazos", "piernas" y "cabeza", lo que complicaría la comprensión del sistema descrito. Un resultado inmediato serían 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 ahora es una matriz.
En ese momento tienes tu configuración inicial. como una matriz. P.ej
std::array
tiene un operador de igualdad:En ese momento su sintaxis se convierte en:
Al igual que la respuesta de Gian Paolo, es breve, clara y fácilmente verificable / depurable. En este caso, hemos delegado los detalles de las expresiones booleanas al compilador.
fuente
No tendrá que preocuparse por combinaciones no válidas de indicadores booleanos si se deshace de los indicadores booleanos.
Claramente tienes tres estados (escenarios). Sería mejor modelar eso y derivar las propiedades booleanas de esos estados, no al revés.
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:
enum
casosswitch
no manejados en declaraciones detectará a los captadores de propiedades que no manejan ese escenario.Este enfoque también tiene el beneficio adicional de ser muy eficiente.
fuente
Mis 2 centavos: declare una suma variable (entero) para que
Verifique la suma con las condiciones que desee y listo. De esta manera, puede agregar fácilmente más condiciones en el futuro, manteniéndolo bastante sencillo de leer.
fuente
La respuesta aceptada está bien cuando solo tiene 3 casos y donde la lógica para cada uno es simple.
Pero si la lógica para cada caso fuera más complicada, o hay muchos más casos, una opción mucho mejor es utilizar la cadena de responsabilidad. patrón de diseño de .
Creas un
BaseValidator
que contiene una referencia a unBaseValidator
y un métodovalidate
y un método para llamar a la validación en el validador al que se hace referencia.Luego, crea una serie de subclases que heredan de
BaseValidator
, anulando elvalidate
método con la lógica necesaria para cada validador.Luego, usarlo es simple, cree una instancia de cada uno de sus validadores y configure cada uno de ellos para que sea la raíz de los demás:
En esencia, cada caso de validación tiene su propia clase que es responsable de (a) determinar si la validación coincide con esa caso, y (b) enviar la validación a otra persona en la cadena si no lo es.
Tenga en cuenta que no estoy familiarizado con C ++. Intenté hacer coincidir la sintaxis de algunos ejemplos que encontré en línea, pero si esto no funciona, trátelo más como un pseudocódigo. También tengo un ejemplo completo de Python funcional a continuación que se puede usar como base si se prefiere.
Nuevamente, puede encontrar esto excesivo para su ejemplo específico, pero crea un código mucho más limpio si termina con un conjunto de casos mucho más complicado que deben cumplirse.
fuente
Un enfoque simple es encontrar la respuesta que crea aceptable.
Sí = (boolean1 && boolean2 && boolean3 && boolean4) + + ...
Ahora, si es posible, simplifique la ecuación usando álgebra booleana.
como en este caso, aceptable1 y 2 se combinan para
(boolean1 && boolean2 && boolean3)
.Por lo tanto, la respuesta final es:
fuente
utilizar campo de bits :
PD :
Es una gran lástima para los CPPers. Pero, UB no es mi preocupación, compruébalo en http://coliru.stacked-crooked.com/a/2b556abfc28574a1 .
fuente
unsigned char*
, aunque creo que simplemente usar algo como((((flag4 <<1) | flag3) << 1) | flag2) << 1) | flag1
probablemente sería más eficiente.