Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Lógica Proposicional para Programación

Conceptos lógicos fundamentales aplicados a contratos, aserciones y verificación de código

Universidad Nacional de Rio Negro - Sede Andina

La lógica proposicional es la herramienta formal que permite razonar sobre la veracidad o falsedad de afirmaciones dentro del software. En programación, su aplicación es directa: desde la escritura de estructuras condicionales simples hasta la especificación y verificación formal de programas mediante contratos (precondiciones, postcondiciones e invariantes).

Esta guía introduce los conectivos lógicos, las equivalencias fundamentales y su correlato con las aserciones en el código.


1. Proposiciones y conectivos lógicos

Una proposición es una afirmación declarativa que puede ser únicamente verdadera (V) o falsa (F). En código, una proposición se representa típicamente mediante expresiones booleanas o variables de tipo bool (en C, #include <stdbool.h>).

Conectivos lógicos fundamentales

Los conectivos lógicos permiten combinar proposiciones atómicas para formar proposiciones compuestas. A continuación se presentan los conectivos principales, su notación lógica y su traducción al lenguaje C:

ConectivoSímbolo lógicoOperador en CDefinición informal
Negación¬P\neg P!PEs verdadera si y solo si PP es falsa.
ConjunciónPQP \land QP && QEs verdadera si y solo si tanto PP como QQ son verdaderas.
DisyunciónPQP \lor QP || QEs verdadera si al menos una de las dos proposiciones es verdadera.
Condicional (Implicación)P    QP \implies Q(No nativo)Representa “Si ocurre PP, entonces se cumple QQ”.
Bicondicional (Equivalencia)P    QP \iff QP == QEs verdadera si ambas tienen el mismo valor de verdad.

2. La Implicación Lógica (P    QP \implies Q) y su rol en Contratos

El condicional o implicación, denotado por P    QP \implies Q, es el bloque constructivo de las precondiciones y postcondiciones en el diseño por contrato.

Tabla de verdad de la implicación

La implicación P    QP \implies Q solo es falsa cuando el antecedente (PP) es verdadero y el consecuente (QQ) es falso. En cualquier otro caso, la implicación es verdadera:

PPQQP    QP \implies QComentario pedagógico
VVVEl antecedente se cumple y la promesa se respeta.
VFFEl antecedente se cumple pero la promesa falla (violación del contrato).
FVVEl antecedente no se cumple; no hay obligación (cumplimiento por vacuidad).
FFVEl antecedente no se cumple; no hay obligación (cumplimiento por vacuidad).

Equivalencia lógica fundamental: Implicación Material

Dado que en C/C++ y Java no existe un operador nativo para la implicación lógica, debés traducirla utilizando conectivos elementales. La ley de implicación material establece la siguiente equivalencia:

P    Q¬PQP \implies Q \equiv \neg P \lor Q

En código, esta equivalencia se traduce de la siguiente manera:

// Para verificar formalmente que P implica Q:
assert(!P || Q);

3. Leyes lógicas esenciales y simplificación de código

El uso de equivalencias lógicas permite reescribir condiciones complejas en el código de forma más legible y eficiente, reduciendo la carga cognitiva.

Leyes de De Morgan

Estas leyes describen cómo distribuir la negación sobre una conjunción o una disyunción:

  1. Negación de una conjunción:

    ¬(PQ)¬P¬Q\neg(P \land Q) \equiv \neg P \lor \neg Q

    En C: !(P && Q) es lógicamente equivalente a !P || !Q.

  2. Negación de una disyunción:

    ¬(PQ)¬P¬Q\neg(P \lor Q) \equiv \neg P \land \neg Q

    En C: !(P || Q) es lógicamente equivalente a !P && !Q.

Ejemplo de refactorización de código

Considerá el siguiente bloque condicional que valida si un puntero no es nulo y el valor de su nodo es válido:

// Condición compleja y redundante
if (!(ptr != NULL && ptr->valor > 0)) {
    // Manejo de error
}

Aplicando la primera Ley de De Morgan, podés reescribir la condición eliminando la negación externa:

¬(ptrNULLptr->valor>0)¬(ptrNULL)¬(ptr->valor>0)\neg(\text{ptr} \neq \text{NULL} \land \text{ptr->valor} > 0) \equiv \neg(\text{ptr} \neq \text{NULL}) \lor \neg(\text{ptr->valor} > 0)

Lo cual equivale a:

ptr==NULLptr->valor0\text{ptr} == \text{NULL} \lor \text{ptr->valor} \le 0

Refactorizando el código:

if (ptr == NULL || ptr->valor <= 0) {
    // Manejo de error
}

4. Ejercicios

Poné a prueba tu comprensión de la lógica proposicional aplicada al código con los siguientes ejercicios.

Solution to Exercise 1

La estructura formal de la afirmación es (PQ)    R(P \land Q) \implies R. Aplicando la equivalencia de implicación material:

(PQ)    R¬(PQ)R(P \land Q) \implies R \equiv \neg(P \land Q) \lor R

Y aplicando De Morgan a la negación izquierda:

¬P¬QR\neg P \lor \neg Q \lor R

En código C, la aserción correspondiente es:

assert(arreglo == NULL || largo <= 0 || arreglo->datos != NULL);

O de forma equivalente usando el paso de implicación material directo sobre la conjunción:

assert(!(arreglo != NULL && largo > 0) || arreglo->datos != NULL);
Solution to Exercise 2

La condición de permanencia del lazo tiene la forma PQP \land Q, donde:

  • PP: i < tamano

  • QQ: arreglo[i] != objetivo

El lazo finaliza cuando la condición de permanencia se vuelve falsa, es decir, cuando ocurre ¬(PQ)\neg(P \land Q). Aplicando la primera Ley de De Morgan:

¬(PQ)¬P¬Q\neg(P \land Q) \equiv \neg P \lor \neg Q

Traduciendo al lenguaje natural y a expresiones condicionales de C:

  • ¬P\neg P: i >= tamano (se llegó al final del arreglo).

  • ¬Q\neg Q: arreglo[i] == objetivo (se encontró el elemento buscado).

Por lo tanto, la condición de salida es: i >= tamano || arreglo[i] == objetivo Esto demuestra que al salir del lazo, o bien se agotaron los elementos, o bien se encontró el objetivo.

Solution to Exercise 3

La invariante debe cumplirse antes y después de cualquier operación pública de la pila. Definimos las proposiciones:

  • PP: tope >= 0

  • QQ: tope <= N

La invariante del TDA es la conjunción de ambas condiciones:

PQ0topeNP \land Q \equiv 0 \le \text{tope} \le N

En C, la aserción de verificación del estado interno de la pila se escribe:

assert(pila->tope >= 0 && pila->tope <= pila->capacidad);