Ilustración conceptual de puertas seleccionadas mediante condiciones agrupadas en un modelo arquitectónico.

Operadores y lógica booleana: construir condiciones que tengan sentido

Una puerta de L01 aparece en el informe de incidencias aunque su código sea correcto. El programa termina sin errores, devuelve una lista y hasta permite exportarla. Qué despliegue de normalidad para haber seleccionado lo que no tocaba. Antes de revisar la API, reinstalar Python o acusar al modelo, yo miraría la condición: quizá el problema ocupe exactamente un par de paréntesis.

En este ejercicio queremos seleccionar puertas de L01 o L02 que necesiten revisión. La necesidad de revisión ya estará calculada y expresada como verdadero o falso; aquí aprenderemos a combinar esa información con el nivel. Trabajaremos con seis registros ficticios y compararemos dos expresiones que parecen decir lo mismo. Al terminar podrás explicar por qué cada puerta entra o queda fuera, algo bastante más convincente que confiar en el número de filas obtenido.

Tabla de contenidos

Una comparación devuelve una respuesta, no cambia el dato

En el artículo de variables utilizamos nombres para conservar valores. Ahora podemos hacerles preguntas. Si nivel contiene "L01", la comparación nivel == "L01" devuelve verdadero. El doble igual pregunta si ambos valores son iguales; el igual sencillo de una asignación guarda un valor bajo un nombre. No son dos maneras de escribir lo mismo, aunque tengan la cortesía de parecerse lo suficiente como para despistarnos.

Un resultado booleano tiene dos valores posibles: verdadero o falso. Python los escribe True y False; C# utiliza true y false. Una comparación entre los datos adecuados produce esa respuesta, que después podemos combinar con otras. En Python, este primer fragmento muestra dos comparaciones y conserva el nivel original.

				
					nivel = "L01"
necesita_revision = False

print(nivel == "L01")
print(nivel == "L02")
print(nivel)
				
			

La salida será True, False y L01. Preguntar si el nivel es L02 no lo ha convertido en L02. Esa separación importa al leer condiciones: una expresión debería dejar claro qué información consulta y qué respuesta calcula. Si dentro de una comprobación escondemos además cambios en los datos, habrá que razonar sobre ambos efectos. Para esta primera selección no necesitamos semejante animación.

También existen comparaciones como distinto, mayor o menor. Elegirlas exige saber qué dato estamos comparando. Una altura numérica puede contrastarse con un límite numérico expresado en la misma unidad; un código de nivel no se convierte en una cota porque contenga cifras. Comparar nombres para deducir un orden físico de plantas introduce una regla adicional que nadie ha explicado. Yo conservaría esa distinción antes de escribir una expresión muy compacta y descubrir después qué significado había decidido atribuirle.

«Y», «o» y «no» tienen un alcance concreto

Para entrar en nuestro informe deben cumplirse dos requisitos: pertenecer a uno de los niveles elegidos y necesitar revisión. La primera parte contiene una alternativa; la segunda restringe el conjunto completo. Si nombramos esas respuestas como nivel_incluido y necesita_revision, la selección será nivel_incluido and necesita_revision en Python. Solo devuelve verdadero cuando ambas respuestas son verdaderas.

La operación «o» permite que baste una de las alternativas. Es inclusiva: también es verdadera si se cumplen las dos. «No» invierte una respuesta booleana. En Python se escriben and, or y not; para estas operaciones con booleanos usaremos &&, || y ! en C#. No necesitamos aprender todos los símbolos del lenguaje antes de leer una selección de puertas. Me conformaría con que los tres que utilicemos expresen la regla acordada.

La tabla siguiente recorre las cuatro combinaciones posibles de dos respuestas. No pretende ocupar el lugar del ejemplo; nos ofrece una referencia pequeña para comprobar cómo se combinarán sus partes. Si A significa «nivel incluido» y B «necesita revisión», la columna A y B será la que decide nuestra selección.

ABA y BA o B
FalsoFalsoFalsoFalso
FalsoVerdaderoFalsoVerdadero
VerdaderoFalsoFalsoVerdadero
VerdaderoVerdaderoVerdaderoVerdadero

Fíjate en la tercera fila. Una puerta del nivel elegido que no necesita revisión debe quedar fuera del informe. Si utilizamos «o» entre ambas condiciones, entrará por pertenecer al nivel. La expresión sería válida para el intérprete y equivocada para nuestra finalidad. La sintaxis decide si el programa puede interpretar la instrucción; el resultado esperado permite juzgar si esa instrucción expresa lo que queríamos.

Los paréntesis deciden a quién se exige la revisión

Volvamos a los nombres concretos. La expresión nivel == "L01" or nivel == "L02" and necesita_revision combina una alternativa con una exigencia. En Python, and tiene prioridad sobre or; por tanto, esa escritura se interpreta como L01, o bien L02 que necesite revisión. Todas las puertas de L01 quedan incluidas, aunque la última respuesta sea falsa. El ordenador no ha relajado el criterio: hemos escrito un criterio distinto con mucha seguridad tipográfica.

Nuestra intención necesita agrupar los dos niveles antes de aplicar la revisión: (nivel == "L01" or nivel == "L02") and necesita_revision. La referencia de expresiones de Python documenta el comportamiento de estas operaciones y su precedencia. Los paréntesis hacen visible qué alternativas forman una unidad, sin exigir que quien mantenga el código recite de memoria la tabla del lenguaje.

Este programa ejecuta ambas expresiones sobre seis casos. Cada registro agrupa un identificador, un nivel y la respuesta booleana ya calculada. for recorre esos registros para mostrar las decisiones; aquí nos interesa comparar las dos columnas, no construir todavía el sistema que obtiene los datos del modelo.

				
					puertas = [
    ("P01", "L01", False),
    ("P02", "L01", True),
    ("P03", "L02", False),
    ("P04", "L02", True),
    ("P05", "L03", True),
    ("P06", "L03", False),
]

for identificador, nivel, necesita_revision in puertas:
    ambigua = nivel == "L01" or nivel == "L02" and necesita_revision
    correcta = (nivel == "L01" or nivel == "L02") and necesita_revision
    print(identificador, ambigua, correcta)
				
			

La columna correcta solo selecciona P02 y P04. P01 muestra la diferencia: la expresión sin agrupación devuelve verdadero y la agrupada devuelve falso. P05 es otro caso útil: necesita revisión, pero pertenece a L03, así que queda fuera de este alcance. Si solo hubiéramos probado P02, ambas versiones habrían parecido funcionar. Una prueba agradecida puede confirmar incluso una condición que selecciona de más.

Podemos mejorar la lectura conservando el resultado de la alternativa en nivel_incluido y combinándolo después con necesita_revision. Un nombre ayuda cuando explica un concepto del proceso. Dividir cada comparación en una variable distinta por obligación puede producir diez nombres que obligan a saltar entre líneas para entender una regla de dos pasos. Yo agruparía por significado, no por el número de operadores que seamos capaces de contar.

C# cambia los símbolos y mantiene la pregunta

La versión siguiente expresa el mismo recorrido en un programa de consola de C#. bool declara la naturaleza de las respuestas y Console.WriteLine muestra los resultados. El conjunto de entradas es idéntico al de Python. Puedes guardarlo como Program.cs en un proyecto dirigido a .NET 8, utilizando la preparación del entorno explicada en los artículos anteriores.

				
					using System;

var puertas = new (string Id, string Nivel, bool Revision)[]
{
    ("P01", "L01", false), ("P02", "L01", true),
    ("P03", "L02", false), ("P04", "L02", true),
    ("P05", "L03", true), ("P06", "L03", false)
};

foreach (var puerta in puertas)
{
    bool ambigua = puerta.Nivel == "L01"
        || puerta.Nivel == "L02" && puerta.Revision;
    bool nivelIncluido = puerta.Nivel == "L01" || puerta.Nivel == "L02";
    bool correcta = nivelIncluido && puerta.Revision;
    Console.WriteLine($"{puerta.Id} {ambigua} {correcta}");
}
				
			

También aquí la expresión agrupada selecciona P02 y P04, mientras la otra añade P01. La documentación de operadores lógicos de C# explica sus símbolos y la evaluación condicional. La equivalencia de este ejemplo se apoya en que las entradas de revisión son booleanas; no demuestra que ambos lenguajes interpreten cualquier objeto de la misma forma.

Ese límite aparece enseguida si una exportación entrega el texto "False". En Python, una cadena no vacía se considera verdadera cuando se pide su valor de verdad. No se interpreta su significado en inglés. En C# tampoco puedes utilizar sin más una cadena como si fuera un bool. La entrada debe convertirse con una regla explícita que decida qué textos admite y qué hace con un valor desconocido. Dejar esa decisión a una conversión genérica puede conseguir que una columna llena de «False» active precisamente la revisión que pretendía desactivar. Un bilingüismo informático bastante poco recomendable.

El orden también puede evitar una operación inválida

Hay una razón adicional para leer las condiciones de izquierda a derecha. En una combinación con and, Python no necesita evaluar la parte derecha si la izquierda ya es falsa. C# ofrece ese comportamiento con &&. En una combinación con or o ||, una primera respuesta verdadera permite detener la evaluación. A ese mecanismo se le llama cortocircuito; no describe una avería, aunque el nombre haya elegido una imagen poco tranquilizadora.

Podemos aprovecharlo al comprobar un texto que quizá sea nulo. Primero preguntamos si existe un valor y después intentamos quitar los espacios exteriores. El segundo paso utiliza un método del texto; no tendría sentido pedirlo sobre el nulo. Este fragmento Python imprime falso sin intentar ejecutar ese método.

				
					codigo = None
tiene_texto = codigo is not None and codigo.strip() != ""
print(tiene_texto)
				
			

En C# podemos expresar esa protección con el mismo orden. Para este ejemplo el contrato de entrada es texto o nulo; no estamos validando cualquier objeto ni resolviendo la ausencia de un parámetro de Revit. Mantener ese alcance evita atribuir a una línea de código el trabajo de un lector de datos completo.

				
					using System;

string? codigo = null;
bool tieneTexto = codigo is not null && codigo.Trim() != "";
Console.WriteLine(tieneTexto);
				
			

Invertir las dos comprobaciones obligaría a utilizar el método antes de saber si el texto existe. También sería un error intercambiar && por & esperando el mismo recorrido: con operandos booleanos, & evalúa ambos lados. La elección del operador afecta al trabajo que llega a ejecutarse. No utilizaría este mecanismo para esconder efectos secundarios; lo utilizaría para mostrar una precondición clara antes de una operación que depende de ella.

Ahora cambia P05 a L02 sin tocar su necesidad de revisión. La selección correcta debe incluir P02, P04 y P05. Después cambia solo P01 para que necesite revisión: también debe entrar. Cada modificación altera una condición reconocible y permite anticipar el resultado. Si una expresión no supera esa conversación sobre seis puertas, todavía no le confiaría una selección de seiscientas. El informe ya tendrá tiempo de ser grande cuando hayamos conseguido que sea el informe correcto.

Artículos relacionados

Respuestas

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *