domingo, 28 de agosto de 2022

Python. Estructuras.

Condicionales en Python

Buscando la simplicidad, Python dispone de una única estructura condicional con tres niveles de concreción. Esta estructura es análoga al Il-ElseIf-Else de OOo Basic, aunque con características propias de la sintaxis de Python.


Estas son sus características específicas y diferenciales:

  • En Python el condicional (al igual que el resto de las instrucciones) se escribe todo en minúsculas.
  • No existe sentencia de finalización, usando como alternativa el sangrado o indentado del código, que es obligatorio en Python, aunque  también recomendable en el resto de los lenguajes.
  • La instrucción ElseIf se simplifica y se expresa como elif.
  • Al finalizar cada una de las instrucciones se deben escribir dos puntos (:)
El funcionamiento del condicional es el mismo que describimos en PSeInt y en OOo Basic
  • Se usa únicamente if cuando no existe o no nos interesa más que comprobar si la proposición cumple la condición que nos interesa.
  • Se emplea else cuando, además de esa condición, nos interesa controlar su alternativa.
  • Y se emplea elif cuando existen varias condiciones posibles para la misma proposición.
NOTA: Python carece de estructura equivalente a Selec Case, así que il-elif-else es nuestra única alternativa disponible para estos casos.

Vamos a trasladar el código elaborado con PSeInt a Python, al igual que hicimos en la entrada anterior con OOo Basic, para mostrar las similitudes y diferencias:


Esta que es la formulación más simple de un condicional, nos permite apreciar tres diferencias básicas, que muestran las características de la sintaxis de Python:
  • La primera, el sangrado (o indentado), obligatorio en Python, como ya dije, nos muestra dos niveles: el primero, el de la sentencia if, el segundo el del bloque de código (en esta caso, una única sentencia print())

  • La segunda, la ausencia de "Entonces"y similares, y su sustitución por (:). Muy importante no olvidarlos.
  • Y la tercera, la ausencia de sentencia de finalización, sustituida, como dije, por el sangrado obligatorio.
Si queremos controlar el caso en que no se cumpla esta condición (que la proposición vEdad >17 sea F), emplearemos la sentencia else.


  • Observa el modo en que se emplea else, qué posición ocupa respecto a if y cómo se mantiene el sangrado en la sentencia que se sigue.
Cuando queremos construir un condicional anidado, debemos mantener la jerarquía que implica este anidamiento mediante el incremento del sangrado, como puedes observar en este tercer ejemplo:
  • Fíjate la posición que ocupa el segundo if respecto al primero: se sitúa a la misma altura que las sentencias print() del if primario, mientras que sus propias sentencias print() se desplazan (sangrado) hacia la derecha (yo usé un tabulador, pero no es necesario que sea esta "medida").
La simplicidad de Python en cuanto a estructura hace que sea muy exigente con el uso del sangrado, lo que genera sus dificultades a la hora de escribir estructuras condicionales anidadas complejas. Por ellos muchos programas de escritura de código suelen generar automáticamente estos sangrados, evitando así errores de "indentación".

Por último, el condicional "Caso" que en pseudocódigo PSeInt se identifica con la instrucción Segun, no tiene equivalente en Python, pero se desarrolla mediante la instrucción elif, del mismo modo que en OOo Basic se utiliza la expresión ElseIf dentro de un condicional If...End If. Reproduzco a continuación el script sobre dias de la semana creado en PSeInt junto con su formulación en Python para que puedas comparar semejanzas y diferencias, y para que tengas un modelo de cómo utilizar la formulación más compleja del condicional en Python.


  Dado que el resto ya es conocido, sólo pedirte que te fijes en la sintaxis de elif:
  • En cuanto a sangrado, se escribe a la misma altura que if.
  • Dado que es una valoración diferente de la proposición (en este caso empleando el comparador de igualdad), tenemos que escribir los términos de la comparación (variable [operador relacional] valor)
  • La línea elif, al igual que if y que else, finalizan con dos puntos (:)
  • La instrucción o instrucciones que sigue a elif deben sangrase u nivel (un tabulador, por ejemplo)

miércoles, 24 de agosto de 2022

Introducción. Lenguajes.

 PSeint. Bifurcación

Veamos cómo emplear estructuras condicionales en pseudocódigo (PSeInt), empezando por la formulación más simple, aquel que permite tomar decisiones en función del cumplimiento de un criterio (condicionales simples),  hasta llegar a estructuras condicionales complejas.

Este script representa la formulación más simple de una estructura condicional (condicional simple): el pseudocódigo y su representación gráfica mediante un flujograma, que, como sabes, es una de las opciones interesantes que presenta este programa:

Definir vEdadTxt Como Caracter;
Definir vEdadNum Como Entero;
Escribir "¿Cuántos años tienes?";
Leer vEdadTxt;
vEdadNum = ConvertirANumero(vEdadTxt);
Si vEdadNum > 17 Entonces
    Escribir "Eres mayor de edad. Puedes acceder";
Fin Si


NOTA: Cuando lo corremos, en PSeInt, tanto en el script como el flujograma se van indicando las líneas de código/los elementos del gráfico que se ejecutan en cada uno de los pasos, lo que permite comprobar visualmente el funcionamiento del algoritmo. Esta utilidad facilita la comprensión del mismo.

Sobre este flujograma me interesa que observes...

A. Sobre el uso de variables y funciones de conversión:

    • He creado dos variables para convertir el contenido de la variable original (de tipo texto) a dato tipo numérico empleando la función ConvertirANumero().
    • Esto es debido a que en determinados lenguajes, la instrucción de entrada (Input) trabaja únicamente con variables de alfanuméricas, siendo necesario su conversión a tipo numérico, en función de procesamiento del que vaya a ser objeto. 
    • Aunque no es el caso de PSeInt, esta formulación permite desarrollar procedimientos de mayor complejidad y exigencia. Además todos los lenguajes cuentan con estas funciones de conversión, por lo que conviene tenerlas presentes y aprender a emplearlas.

B. Sobre la estructura condicional:

    • La proposición que se analiza tras el Si condicional [Variable > Valor -> True]: en este caso se plantea la comparación de la variable en términos cuantitativos (>) y en relación al valor numérico de referencia (17).
    • La opción en función del resultado del condicional: si se cumple el criterio de verdad, esto es, si el contenido de vEdadNum es > 17, entonces se desarrolla la opción (en este caso en forma de mensaje de salida - Output) y finaliza el condicional (en este caso también el script).
    • En caso contrario, esto es: si Valor es > 17 es Falso, que se concreta en si la variable vEdadNum tiene asignado un valor inferior a 18, finaliza el condicional (en este caso también el script).
Por otra parte, si expresamos el condicional en forma de diagrama de Venn, tendremos la siguiente representación de la relación de pertenencia del elemento opción al conjunto de opciones que llamamos O y de la inclusión de este conjunto al universo de opciones que llamamos U. Como puedes ver, la simplicidad de la estructura y de sus componentes, determina que el elemento o sea único, al igual que el subconjunto O.


Este diagrama permite  comprender que:
  • El conjunto puede estar formado por n elementos, sin que esto afecte a la estructura condicional. 
  • Podemos identificar tantos subconjuntos  de O como sea necesario, aunque en esta estructura únicamente identificamos uno (O1) que mantiene relación de inclusión con O
  • También se conceptualiza como subconjunto de U
  • El complementario de (O') es (en esta caso concreto) un conjunto vacío, pero no necesariamente, lo que abre multitud de opciones.
  • Interesa entender la relación O vs O' como una relación de complementariedad a la vez que de oposición, ya que aquí radica el potencial de opcionalidad de ambos conjuntos, de la que se deriva la propia estructura condicional.
Como puedes comprender, esta estructura, aunque útil en determinadas circunstancias, es demasiado simple, teniendo en cuenta la complejidad que permites vislumbrar la conceptualización matemática y su representación mediante el diagrama de Venn anterior. 

Por suerte, es posible contar con estructuras más complejas, como la que representa la instrucción Si...SiNo... Fin Si (If...Else...End If en determinados lenguajes de programación), que incluye a la anterior, a la vez que la modifica. En PSeInt esta es la estructura aplicada al ejemplo anterior:

Si vEdadNum > 17 Entonces
    Escribir "Eres mayor de edad. Puedes acceder";
SiNo
    Escribir "No eres mayor de edad. No te está permitido el acceso"  
Fin Si

Esta estructura, además de permitirnos un mínimo de consideración con quienes no cumplen el criterio de base, nos abren un universo de opciones, en realidad un subconjunto, siguiendo la formulación de la teoría de conjuntos, que identificamos como conjunto (de opciones) complementario del conjunto O, nuestro anterior O'.

Definiendo la complementariedad O - O' como el resultado de la diferencia de con el conjunto (O' = O/U), que supone la siguiente condición:

En este caso particular, el complementario de OO' es un conjunto formado por un único elemento (o'1), pero podría estar formado por n elementos, siendo una cantidad entre 0 e infinito. 

NOTA: Desde esta perspectiva, la primera formulación del condicional sería un caso especial del actual, en el que O' es conjunto vacío.

Mostrado el potencial de la conceptualización matemática de las estructuras condicionales, aunque de forma elemental, volvemos a la lógica de programación con la expresión gráfica del script actual: 


Este flujograma muestra la doble opcionalidad, resultante de la valoración (V/F) de la proposición básica (vEdadNum > 17).
  • La primera rama resulta de la condición V y coincide con el conjunto O
  • La segunda, que inicia con SiNo es el conjunto de opciones alternativas y por defecto, para cuando la proposición cumple la condición F y representa el conjunto complementario O' 
Una vez vistas las estructuras condicionales simples, es momento de tratar sobre las condicionales complejas, las cuales, como vimos en la entrada anterior, son de dos tipos: las anidadas o jerárquicas, y las no jerárquicas.

Las primeras están determinadas por la previa pertenencia (común) al conjunto formado por los elementos que cumplen el criterio x (siendo x V o F), con posterior bifurcación en función de la relación de oposición/complementariedad  respecto a un segundo criterio y (siendo y V o F), que determina la diferenciación del conjunto origen en dos subconjuntos. La representación mediante diagrama de Venn puede ser la siguiente:

Téngase  en cuenta que:
  • Oy O2 quedan definidos como subconjuntos de O, que se define en relación a un condicional primario (C1)  por lo que comparten la relación de complementariedad/oposición con O'. Esto implica que forman parte del conjunto de sentencias/opciones que derivan del condicional primario.
  • Oy Opueden presentar intersección (elementos comunes) o ser ésta conjunto vacío, pero lo que importa en términos de estructura condicional es que presentan relación de oposición/complementariedad entre sí, esto es: que están definidos como subconjuntos de el función del resultado de un segundo condicional (C2), identificándose Ocon las opciones resultantes de C2 ->V y Ocon C2 ->F.
  • Por ello, lo que interesa es la complementariedad entre las opciones (elementos) que conforman Oy O2, esto es: el subconjunto de elementos resultantes de la diferencia entre la unión de ambos subconjuntos menos su intersección, que no es otro que el subconjunto definido en O complementario de la intersección de Oy O2
  • Si la intersección O1 con Ono es conjunto vacío, este subconjunto será tratado como parte de la condicional C1 pero al margen de la condicional C2, por lo que a efectos de ésta, Oy O2 se consideran conjuntos disjuntos.
  • El anidamiento de condicionales, al igual que la subdivisión de subconjuntos en subconjuntos, está limitada únicamente por el número de elementos que lo conforman, por lo que la estructura de anidamiento puede repetirse sucesivas veces. Que esto se produzca en la práctica dependerá tanto del número de elementos disponibles en el subconjunto definido por la formulación de la proposición, como por la propia posibilidad y pertinencia de formular éstas.
En un algoritmo el anidamiento de condicionales se realiza en función de la pertinencia de la formulación de proposiciones susceptibles de análisis condicional, lo cual viene dado por su contenido, por el problema que se trate de resolver. Un ejemplo (simple) de anidamiento puede ser la opcional secundaria en función de una opcional primaria, como la que se presenta en este caso, en el que  C1 refiere a la mayoría de edad y C2 a la existencia de permiso de la familia.

Proceso CondicionalAnidadoIzqda

Definir vEdadNum Como Entero;
Definir vPermisoFamilia Como Logico;
        Escribir 'Edad del/de la solicitante?';
Leer vEdadNum;

Si vEdadNum > 17 Entonces
Escribir 'Al ser mayor de edad, es posible la inscripción al curso';
SiNo
Escribir "Es menor de edad.";
Escribir "¿Consta permiso de la familia? Verdadero o Falso";
Leer vPermisoFamilia;
Si vPermisoFamilia = Verdadero Entonces
Escribir "Aun así, al constar permiso familiar, es posible la inscripción al                                  curso.";
SiNo
Escribir "Al no constar permiso familiar, se deniega la inscripción al curso.";
FinSi

FinSi

FinProceso

El flujograma correspondiente es el siguiente:


 NOTA: En este caso, al realizarse el condicional anidado sobre el complementario del condicional primario (esto es, sobre O'), el diagrama de Venn que lo representa sería el siguiente (o cualquier representación análoga):


La segunda estructura condicional compleja se caracteriza por la ausencia de jerarquía, lo que implica que no existe un segundo nivel de condicionalidad (C2), situándose la opcionalidad múltiple a nivel de C1. Esto significa que en C1 se pueden formular/presentar n proposiciones, todas ellas susceptibles de ser V o F, pero con la característica de ser mutuamente excluyentes: si la proposición C1a resulta V, no se plantean las siguientes C1nentendiéndose, en consecuencia, que sí y sólo sí C1a resulta F, entonces se plantea la proposición C1b y así sucesivamente hasta C1n.

PSeInt cuenta con dos estructuras condicionales: la utilizada antes y la empleada en este script:

Proceso IdentificaCurso
Definir vCursoPrimaria como Entero;
Definir vExpresion como caracter;
Escribir "¿En qué curso estás?. Escribe el número";
Leer vCursoPrimaria;
Segun vCursoPrimaria Hacer
1:
vExpresion <- "cursa 1º de ";
2:
vExpresion <- " cursa 2º de ";
3:
vExpresion <- " curas 3º de ";
4:
vExpresion <- "cursa 4º de ";
5:
vExpresion <- " cursa 5º de ";
6:
vExpresion <- "cursa 6º  de ";
De Otro Modo:
vExpresion <- " no cursa ";
Fin Segun
Escribir "El alumno o alumna " + vExpresion + "E. Primaria";
FinProceso

... y que expresa este flujograma:


Tanto el script como su representación gráfica se pueden expresar mediante un diagrama de Venn como el siguiente...


... que, aunque reduce las opciones, muestra lo esencial de la estructura:
  • La existencia de un conjunto complementario del resultante de la unión de todos los subconjuntos (de opciones), representado en el diagrama de Venn por la zona amarillenta y en el código por la instrucción De Otro Modo:, equivalente a Else.
  • La existencia de subconjuntos de opciones excluyentes entre sí, que no comparten áreas de intersección (conjuntos disjuntos), por lo que si se comprueba la veracidad de la primera (vCursoPrimaria = 1 -> V), se excluyen todas las demás, y si resulta falsa, se prosigue con la segunda, y así hasta agotar las n posibilidades proposicionales.
NOTA: La expresión de la instrucción (Segun vCursoPrimaria Hacer) y el modo en que se expresa cada caso no se corresponde con la forma en que la acabamos de expresar, pero ésta representa el procedimiento de análisis que aplica, aunque se asemeja a la forma en que se expresaría mediante ElseIf y expresiones equivalentes, de las que carece PSeInt.

Esta representación y su codificación se adapta al problema que se plantea resolver con este algoritmo: "Expresar el nivel que cursa el alumno o alumna", por ejemplo, como parte de la elaboración de un documento/informe en función de un cuestionario. Este ajuste es debido:
  • Evidentemente, al carácter excluyente y dicotómico de la proposición original
  • Y a la simplicidad del conjunto de opciones resultantes de la confirmación de verdad de cada proposición: vExpresion <- "cursa xº de ".
... pero está muy lejos de dar respuesta a opcionalidades complejas en las que existe posible interrelación entre el contenido de las proposiciones (las variables o causas, según contextos) y existe multiplicidad de opciones/posibilidades (los atributos o las consecuencias, según contextos), así como intersecciones entre los conjuntos (causales) que conforman. Una representación gráfica de esta complejidad teórica (pero frecuente en contextos de probabilidad y causalidad) es la que representa el siguiente diagrama de Venn, en el que, a los elementos observables en el diagrama anterior (ver arriba), se añade la presencia de diversos subconjuntos resultantes de las intersecciones que se producen entre los conjuntos de opciones/consecuencias: a mayor número de conjuntos (de factores que inciden o variables causales, por ejemplo), más elevado es el número y la complejidad de las intersecciones. 


Dentro de la lógica proposicional, para la primera cuestión (la complejidad factorial/causal) disponemos de los operadores lógicos que nos permiten reducir el número de opciones. Para la segunda debemos considerar la conveniencia de combinar las dos estructuras condicionales complejas vistas. 

En realidad, como planteamiento teórico abstracto lo anterior no sólo es (teóricamente) correcto, es también práctico, pero es necesario trasladarlo a la práctica, lo que implica analizar el problema para plantear un algoritmo adecuado al mismo, lo que requiere análisis y estudio. 

En este proceso de definir qué estructuras emplear y cómo hacerlo, la formulación del algoritmo en pseudocódigo y el uso de diagramas de flujo son recursos necesarios y herramientas como PSeInt dan respuesta a estas necesidades. Pero puede no ser suficiente lo que nos aportan cuando el problema presenta una elevada complejidad. Ahí es donde la ayuda de la lógica matemática, la teoría de conjuntos y su representación mediante diagramas de Venn, y otras formulaciones en términos matemáticos pueden ser de ayuda.

lunes, 22 de agosto de 2022

Introducción. Lenguajes.

Condicionales. Concepto

Vistos los principales tipos y operadores, nos enfrentamos a la decisión de seguir la lógica de programación, que nos lleva a tratar sobre las funciones que se pueden aplicar a cada uno de los tipos, o a la lógica pedagógica, que  nos lleva a hablar de las principales estructuras que permiten el tratamiento de datos.


Dado el objetivo de este blog, he considerado conveniente seguir la segunda vía, dejando para más tarde el tema de las funciones y su uso respecto a los tipos simples. De este modo podremos apreciar mejor la utilidad de los lenguajes de programación para la creación de algoritmos.

Empezaré por las estructuras condicionales más básicas y su concreción en pseudocódigo y en los lenguajes OOo Basic y Python.

Aunque responde al tratamiento de cualquier variable, un condicional está relacionado funcionalmente con el resultado de la aplicación de los operadores lógicos y relacionales, dado que se basa en la valoración en términos de V/F respecto al contenido de la variable (la proposición que implica) y/o a la comparación entre dos proposiciones (y las variables que las representan).

La estructura del condicional más simple (SI condicional) permite responder V/F a la validez del enunciado al que se refiere: Si vA = x es V (True), entonces y -> Fin del condicional. Si aV = x es F (False), entonces Fin del condicional. Este razonamiento se expresa gráficamente mediante este flujograma:

Una segunda formulación del condicional incluye la alternativa para el caso en que la proposición valorada resulte ser falsa. Esa opción se desarrolla como sigue: SI Proposición es V Entonces A -> Fin del condicional; en caso contrario (Proposición es F), entonces B -> Fin del condicional. La representación mediante flujograma es la siguiente:

Aunque aparentemente hayamos considerado la existencia de un único elemento-opción en cada rama de la estructura, observa que lo denominamos como bloque (de opciones), lo que da a entender que es posible tanto que ese bloque se reduzca a un único elemento, como que esté formado por n elementos. Si bien esto hace más complejo el contenido, esa complejidad se limita a la estructura del propio bloque, pero no afecta a la estructura del condicional.   

Ahora bien, conforme vayamos considerando un número mayor de opciones, esa estructura condicional se va haciendo más compleja, por lo que es necesario disponer de formulaciones condicionales que hagan posible esta complejidad. Esto es posible mediante el anidamiento de condicionales y/o mediante condicionales múltiples.

El anidamiento consiste en la inclusión de un condicional (o varios) dentro de otro condicional. Si forma más simple sería la que representamos en este flujograma...


... que no da idea de la complejidad que puede llegar a alcanzar una estructura de opcionalidad basada en condicionales anidados.

Los condicionales múltiples se basan en la posibilidad de que la condicional inicial pueda ser múltiple (no dicotómica), por lo que no es suficiente con las instrucciones if else. Para cubrir esta función contamos con la instrucción Else If (elif y otras formulaciones), que suponiéndose como alternativas  a If se sitúan a su mismo nivel y mantienen entre sí la misma relación de oposición/complementariedad que mantienen con el If inicial. Dicho de otro modo: no son condicionales anidados, aunque sea posible suplirlos por condicionales anidados y, a la vez, también lo sea la combinación de ambos procedimientos.

El siguiente flujograma representa una estructura condicional múltiple que, en sentido estricto no se formula en términos de Si...entonces, sino en términos de En caso de...


En algunos lenguajes (OOo Basic, por ejemplo), además de la opción If...Else If...Else, también existen estructuras específicas tipo Select Case/Switch case, que obedecería, en sentido estricto al flujograma anterior, pero otros (como Python, por ejemplo) no cuentan con esta estructura específica, siendo necesario utilizar la estructura if compleja (if...elfi...else) que, aunque sea homologable a Selec case, también en sentido estricto, puede entenderse como una especie de anidamiento en cascada, por lo que la representación mediante flujograma podría ser más bien la siguiente:


NOTA: Como veremos en la entrada que sigue, en Pseudocódigo (concretamente en PSeInt) se utilizan dos estructuras condicionales: la simple (Si...SiNo) y la compleja (Según(caso)...Hacer), pero no la estructura compleja derivada de la simple (Si...SiNo_Caso...SiNo) por considerar que ésta es equivalente a la anterior.

Dada la complejidad y la opcionalidad existente en estructuras condicionales y en lo que éstas revelan (la posibilidades de bifurcación del algoritmo), es conveniente explorar las posibilidades que ofrece la conceptualización de la condicionalidad en términos de teoría de conjunto. Evidentemente, esto es posible en cualquier situación en la que se plantee la opcionalidad, pero especialmente cuando ésta resulte ser especialmente compleja.

Debemos partir, para entender esta conceptualización, de la asimilación de la tarea/opción como elemento de un conjunto N formado por los n elementos u opciones. A partir de aquí, podemos entender ese conjunto N como subconjunto perteneciente al universo (conjunto U) de las opciones posibles y, en consecuencia, también la posible existencia de otro conjunto N' que mantiene con N relaciones de complementariedad, si bien no en sentido estricto, ya que es posible la existencia de un subconjunto NN' intersección de N con N' diferente del conjunto vacío. No obstante, si U está formado por dos subconjunto, a efectos prácticos el subconjunto NN' puede considerarse al margen de la opcionalidad, dado que, al pertenecer tanto a N como a N' no son objeto de decisión/elección, de lo que se deriva que N y N' pueden considerarse complementarios a los efectos de la opcionalidad (y por tanto definirse como opuestos N vs ¬N).

Este planteamiento permite conceptualizar las diversas estructuras de opcionalidad, formalizarlas y representarlas gráficamente mediante diagramas de Venn, complementarios ambos al pseudocódigo y a los flujogramas. Repito que cuando la estructura de opciones es simple (aunque pertinente) esta conceptualización no es necesaria, pero se vuele muy útil cuando se incrementa la complejidad, ya que es fácil perderse en la multiplicidad de opciones condicionadas que se presentan en estos casos.

Podremos complementar este análisis con el resultante de la relaciones entre los contenidos de las opciones, que pueden entenderse (y formalizarse) desde la óptica del producto cartesiano como marco general y las relaciones (y tipos de relación) que se establecen entre los elementos (opciones) que conforman los conjuntos de que se derivan de la opcionalidad. Pero este es un segundo tipo de análisis, aunque relacionado con el primero (el estructural). 

Por el momento, parece suficiente con lo comentado aquí para iniciar el trabajo con las estructuras condicionales. Te recomiendo continuar con pseudocódigo (PSeInt), antes de adentrarte en su formulación en lenguajes de programación. Así tendrás una visión general, complementaria de lo que esta entrada te pueda haber aportado, y ya más próxima a la elaboración de algoritmos.

Introducción. Lenguajes.

PSeint. Operadores lógicos

Todos los lenguajes de programación cuentan con una variable de tipo lógico y con operadores lógicos. La variable tiene como característica específica que, al contrario de las variables alfanuméricas y numéricas, sólo admite dos valores: verdadero (True -> 1) o falso (False -> 0), por lo que sirve para almacenar el resultado de la aplicación de los operadores relacionales y los operadores lógicos al resto de las variables, esto es: la variable lógica responde a la lógica proposicional y a sus leyes, que, cómo sabemos, es de carácter dicotómico. El análisis de cómo utilizar y cómo responden este tipo de variable y de operadores en pseudocódigo (PSeInt) nos permitirá entender mejor cómo utilizarlos en lenguajes de programación.

Para empezar, una variable lógica se define en PSeInt como tal y presenta la restricción básica de este tipo: sólo admite como valores (datos) Verdadero o Falso. Si tratamos de darle otros contenidos (incluso "Verdadero" o "Falso", o sus correspondientes numéricos, 1 ó 2), nos genera un error por no coincidencia de tipos.

Proceso TipoLogico1

Definir vA, vB Como Logico;

vA <- Verdadero;

vB <- Falso;

FinProceso

En cuanto a operadores, PSeInt dispone de los tres operadores lógicos más frecuentes: Y (AND), O (OR) y NO (NOT), que, como sabemos, devuelven valores booleanos V/F, al igual que los operadores relacionales, pero en función de los propios criterios que los definen. Mientras que los relacionales establecen juicios V/F en función de la comparación cuantitativa que definimos entre dos variables (Igualdad vs Diferencia, inecuación mayor/menor que...), los lógicos remiten a las tablas de verdad que se identifican entre dos variables en función del tipo de operador: 
  • Y lógico sólo es V si lo son tanto A como B, en el resto de los casos devuelve F.
  • O lógico sólo es F si lo es tanto A como B, en el resto de los casos devuelve V.
  • NO lógico se comporta como el inverso de A, de modo que si A es V, NO A es F y viceversa.
Este es el resultado que obtendremos si comparamos, por ejemplo, dos variables numéricas:

 Proceso TipoLogico2

Definir vA, vB Como Entero;
Definir vV1, vV2 como Logico;
Definir vVL1, vVL2 Como Logico;
vA <- 25;
vB <- 25;
vV1 <- vA = vB;
vV2 <- vA < vC;
Escribir vV1;             -> Devuelve V
Escribir vV2;             -> Devuelve F
vVL1 <- vV1 Y vV2;
vVL2 <- vV1 O vV2;
Escribir vVL1;            -> Devuelve F
Escribir vVL2;            -> Devuelve V
FinProceso

Obsérvese que las variables lógicas podemos utilizarlas para almacenar el resultado de comparaciones, como en el caso de vV1 y vV2, o del uso de conectores u operadores lógicos, como en el caso de las variables vVL1 y vVL2. Esto permite analizar operaciones complejas, resultantes de otras operaciones.

Veamos el comportamiento en Python...

vA = 24
vB = 24

vV1 = vA == vB
vV2 = vA < vB
print(vV1)
True
print(vV2)
False

vVL1 = vV1 and vV2
vVL2 = vV1 or vV2 
print(vVL1)
False
print(vVL2)
True

... que, con las diferencias propias de la sintaxis de Python, es igual que en PSeInt, lo que nos permite comprobar que diferentes lenguajes presentan similar comportamiento y manejo de las variables y los conectores lógicos, algo que no observamos en los operadores relacionales.

Introducción. Lenguajes.

Operadores lógicos

Para finalizar la descripción de los tipos (datos y variables) y los operadores, corresponde ahora presentar los tipos lógicos y los operadores relacionados. Pero antes es necesario retomar algunas cuestiones relativas a la lógica, especialmente a la lógica matemática y a la teoría de conjunto.


En este recorrido, lo primero que debemos retomar es el concepto de proposición que, como podremos ver, tiene diferentes significados.

Según la RAE, el término proposición puede ser entendido como sinónimo de oración, más concretamente como oración simple que se une a otras para formar oraciones compuestas; pero también se entiende por proposición el contenido de su enunciado, respecto al cual se puede emitir un juicio dicotómico en términos de verdad o falsedad. Desde esta perspectiva más centrada en la semántica que en la forma, una proposición es la relación semántica que se produce entre sus dos términos constituyentes (sujeto y predicado), de modo que posible emitir un juicio que los relaciona: bien se afirma o se niega el predicado respecto al sujeto, bien se incluye o excluye el sujeto del predicado.

La primera relación establece una opción dicotómica: V vs. F (toda proposición puede ser verdadera o falsa), que reduce el concepto proposición a determinadas formas expresivas: un enunciado desiderativo o imperativo no son proposiciones, ya que de ellos no se deriva posibilidad de afirmar o negar su enunciado.

Pero la segunda relación, o mejor dicho, la segunda forma de expresar la relación entre el sujeto y el predicado de una proposición, nos permite relacionar su análisis con la matemática, y más concretamente con la teoría de conjuntos (lógica matemática), tanto en términos de relación de pertenencia del elemento al conjunto, como en términos de relación de inclusión entre un conjunto y sus posibles subconjuntos (especialmente el conjunto varío y los unarios).

Además la teoría de conjuntos también permite expresar las relaciones que se producen entre dos o más conjuntos, sean éstas de igualdad o desigualdad. Los operadores relacionales vistos en la entrada anterior permiten establecer relaciones en esos términos entre dos variables (que asimilamos a expresiones que remiten a proposiciones), tanto en términos de igualdad vs. desigualdad (= vs. <>) como en las formas específicas en las que se expresa la desigualdad (esto es: como inecuación): < vs. cuando no se incluye el extremo en el subconjunto que se define, y <= vs. >= cuando sí se incluye.

Es por ello por lo que estos operadores devuelven valores V vs. F como resultados de los juicios resultantes de su aplicación, según pudimos comprobar en la entrada anterior.

Desde esta misma perspectiva, podemos considerar los tipos lógicos como contenedores del resultado de las comparaciones establecidas mediante los operadores relacionales, lo que permite realizar procesos de análisis mucho más complejos.

También los operadores lógicos suponen un avance en esta línea, aunque su comprensión exige que nos adentremos un poco más en la teoría de conjuntos y en la lógica matemática.

Si podemos establecer un juicio dicotómico V-F respecto a la pertenencia de un elemento a un conjunto, o respecto a la inclusión de un subconjunto en un conjunto, también podemos realizar análisis en esos mismo términos (V-F) respecto a las relaciones que se establecen entre dos conjuntos diferenciados, esto es: que no cumplen criterios de inclusión o relación jerárquica. Esto es: conjuntos que mantienen entre ellos una relación que permite enjuiciar la existencia de elementos comunes (intersección), la suma o unión de ambos (unión) o una relación de complementariedad.

Obsérvese que la primera de estas relaciones, la inclusión, es expresión del concepto lógico Y (AND), compartiendo ambas las mismas restricciones para cumplir criterios de verdad y similares signos:

  • Un elemento pertenece a la intersección de dos (o más) conjuntos sí y sólo sí pertenece al primero y (también) al segundo.
  • AND es V sí y sólo sí la proposición A es V y (también) la proposición B es V
Por su parte la relación Unión (de conjuntos) se asocia con el O lógico (y mantiene simbología similar) y permite juicios de verdad mucho más laxos:
  • Un elemento pertenece a la unión de dos (o más) conjuntos, con tal de que pertenezca al menos a uno de ellos.
  • OR es V con tal de que la proposición A sea V o lo sea la proposición B
Finalmente NOT (NO) permite negar la proposición (o la relación proposicional) afirmada previamente, de modo que si A -> V, ¬A -> F, y viceversa. ¬A puede considerar, por tanto, como complementario de A y cumple los mismos criterios que satisface el conjunto complementario (¬A) de A
  • Si un elemento pertenece al conjunto A, no pertenece a su complementario. Si pertenece al complementario (¬A), pertenece a A.
  • Si la proposición A es V, entonces su contraria (NO-A) es F y viceversa.
Además NOT también nos permite valorar (e invertir) el resultado de comparaciones: si (A AND B) es F -> que ¬(A AND B) es V.

Todos estos operadores se rigen (y permiten elaborar) tablas de verdad como las que puedes consultar en este documento.