lunes, 22 de agosto de 2022

OOo Basic. Estructuras.

Bifurcación condicional.

Vistos los principales tipos de datos (y variables) y de operadores, aunque cabe la opción de avanzar explicando las funciones con las que trabajar con los anteriores, dada la complejidad de ese contenido y su amplitud, he considerado más adecuado estudiar las estructuras fundamentales con las que nos podemos encontrar en un algoritmo.



Tres son las formas en que se desarrolla un algoritmo: sucesión lineal (que es la que deriva de lo que hasta ahora sabemos), bifurcación e iteración. Para el desarrollo de la primera es suficiente con el uso de variables y su tratamiento con operadores, pero para las otras dos se necesita contar con estructuras (instrucciones) específicas. Concretamente, para la bifurcación necesitamos estructuras condicionales, que son las que trataremos en este apartado de la sección en la que nos encontramos.

Una definición de bifurcación en programación es la ruptura condicionada del orden lineal del desarrollo del algoritmo; algo así, y en su forma más simple, como un punto en el que se presentan varias opciones de solución/desarrollo por las que hay que optar en función del cumplimiento o no de determinada condición. El cumplimiento o no de la condición (la cual puede ser simple o compleja) determina que se desarrollen unas opciones u otras. Una vez superado ese punto de decisión, el algoritmo puede seguir su curso lineal, hasta que se vuelva a presentar una nueva posibilidad de opción, o quedar todo el condicionado a la decisión previa, siendo esto secundario para lo que ahora nos ocupa.

La forma en la que en OOo Basic se trata la bifurcación es mediante diferentes estructuras condicionales, desde formulaciones (y representaciones) simple a otras más complejas, incluyendo dentro de las segundas el uso de condicionales dentro de condicionales (condicionales anidados). De todos ellos hablaremos en las entradas que se tratan dentro de esta sección. Ahora las presentaré en síntesis, para adelantar contenido y facilitar la comprensión del conjunto y de las partes.

La estructura 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 conjunto de proposiones -> Fin del condicional; pero Si aV = x es F (False), entonces (directamente) 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, la cual cumple la función de opción por defecto. 

Se desarrolla como sigue: SI vA = x es V Entonces Proposiciones A -> Fin del condicional; en caso contrario (ELSE) (vA = x es F), entonces Proposiciones alternativas por defecto B -> Fin del condicional. La representación mediante flujograma es la siguiente:

Aunque aparentemente hayamos considerado la existencia de una única proposición, lo que en el flujograma se denomina Bloque puede estar compuesto por un amplio conjunto de proposiciones, sin que esta complejidad afecte a la estructura condicional. Tampoco se ve afectada por el grado de complejidad que pueda presentar la condición, aunque aquí, por simplificar, la hayamos expresado de la forma más simple posible (aV = x).

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

El anidamiento consiste en la inclusión de un condicional (o varios) dentro de otro condicional. Su forma más simple sería la que representamos en este flujograma que nos da idea, no obstante, 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 y Else. Para ello contamos con la instrucción ElseIf (Else If también es válido), que funciona como alternativa  a If inicial y también concreta (al igual que éste) una condición específica en oposición a Else, que funciona como opción por defecto; de hecho ElseIf requiere tanto la condición como la sintaxis condicional completa (ElseIf vA > x Then).

El siguiente flujograma representa una estructura condicional múltiple.


En algunos lenguajes (OOo Basic, por ejemplo), además de la opción If...ElseIf...Else, también existen otras estructuras específicas (Select Case/Switch case) que responden al flujograma anterior, pero otros lenguajes (Python, por ejemplo) no cuentan con estas instrucciones alternativas sin que se vea reducida la funcionalidad del algoritmo.

OOo Basic. Datos y operadores

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 <= / >= 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 vs. 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 considerarse, por tanto, 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 implica 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.

domingo, 21 de agosto de 2022

OOo Basic. Datos y operadores.

Operadores relacionales.

Cuando necesitamos comparar el valor asignado a dos variables empleamos los operadores relacionales, los cuales devuelven Verdadero (V o 1) si el resultado de la comparación es positivo o Falso (F o 0) si ese resultado es negativo.


Los principales operadores relacionales son los siguientes:

  • De igualdad (= o ==, según el lenguaje), que valora V/F en función de que el contenido de de las dos variables sea el mismo.
  • De diferencia ( !=<>), el opuesto del anterior.
  • De superioridad: Mayor (>) o igual o mayor (>=), que compara dos variables y determina V/F en función de que la primera sea más grande o cuanto menos igual de grande (en el caso de >=) que la segunda.
  • De inferioridad: Menor (<) o igual o menor (<=), que realiza la misma comparación que los anteriores, pero ahora en relación al juicio de inferioridad.
En PSeInt y también Python, los comparadores relacionales son los mismos, aunque algunos de ellos se representen mediante símbolos diferentes.
  • El comparador de igualdad se representa con = en pseudocódigo (PSeInt), coincidiendo con el signo del operador de asignación. En Python se emplea el doble signo de igualdad (==) para evitar esa posible confusión.
  • El opuesto al anterior, el comparador de diferencia se expresa de dos formas diferentes en PSeInt (<> o !=), aunque se puede convertir automáticamente al símbolo matemático de diferencia () según configuración.
  • El segundo de estos símbolos (!=) es que que empleamos en Python.
  • El resto de los operadores (< vs. y <= vs. >=) son iguales en PSeInt y Python.
Por lo que parece, los operadores relacionales no presentan especial dificultad, pero es conveniente analizar su comportamiento cuando los empleamos en la comparación de los dos tipos de variable que conocemos hasta el momento: variables alfanuméricas (o de texto) y los distintos tipo de variables numéricas. 
  • Sólo cuando dos variables (var1 y var2) son del mismo tipo (sea éste alfanumérico o numérico) y tienen asignado el mismo dato, existe coincidencia (V) entre los operadores de igualdad (=), superioridad (>=) o inferioridad (<=), dado que los tres devuelven V. Y es que en realidad se trata de la misma situación: var1 = var2.
  • En caso de variables alfanuméricas, = es V sí y sólo sí ambas tienen asignado exactamente el mismo contenido. Un cambio, por mínimo que sea (vg. un espacio en blanco en Var1 ausente en Var2), aunque no cambie el significado del contenido (vg, el uso de mayúsculas) o sea admisible desde el punto de vista del significado (vg, en caso de que la presencia/ausencia de tilde no afecte al significado), da como resultado que la comparación resulte F y la comparación <> resulte V.
  • No es posible comparar entre variables de diferente tipo (vg, alfanuméricas con numéricas). En estos casos, Python resuelve la igualdad (==) como F y la diferencia (!=) como V.
  • En las variables numéricas, aunque una variable sea de tipo diferente a otra (Entero vs Real) son posibles todas las comparaciones.

lunes, 8 de agosto de 2022

OOo Basic. Datos.

Datos numéricos.

Después de trabajar sobre las variables alfanuméricas, corresponde ahora hacerlo sobre las numéricas. En primer lugar hablaremos de los números y de la estructura de los conjuntos numéricos.



La representación de los números es tan antigua o más que la del lenguaje; de hecho, las primeras representaciones escritas lo son de registros cuantitativos, conteos y operaciones relacionadas con el manejo de cantidades: lo que primero movió al ser humano a escribir no fue tanto "contar" historias como contar cabezas de ganado o  cantidades de cereales.

Por su parte, la RAE define el número como la expresión de una cantidad con relación a su unidad. La palabra cantidad, refiere al número como concepto matemático que la expresa, aunque también como el signo con el que se expresa esa cantidad.

Los números se organizan en conjuntos (conjuntos numéricos) que están estructurados jerárquicamente de modo que unos conjuntos, los más específicos, forman parte de otros conjuntos más generales, lo que remite al concepto de inclusión de conjuntos.

Partiendo de los números naturales (N), el desarrollo del pensamiento matemático ha ido complejizando esta estructura de los conjuntos numéricos hasta llegar a la actual,  cuya representación recoge el siguiente esquema...

https://sites.google.com/site/portafoliofundamentosm/

... aunque es posible que la representación mediante un diagrama de Venn resultar, en mi opinión, más adecuada para expresar la relación existente entre los diferentes conjuntos...


https://cafecito.app/matemateando/

... y la relación que se establece entre ellos expresada mediante la expresión simbólica del concepto inclusión.

https://sites.google.com/site/portafoliofundamentosm/

Esta estructura de conjuntos y las relaciones de inclusión que se establecen entre sus componentes es fundamental para el buen conocimiento de los datos numéricos y de los tipos de variables a ellos asociados, según se contemplan en los diferentes lenguajes de programación.

La principal utilidad de los números es que nos permiten realizar diferente operaciones, unas consideradas básicas (suma, resta...), otras de mayor complejidad (potencia, radicación...), las cuales mantienen relaciones de complementariedad/oposición entre ellas, involucran dos cantidades o colecciones de cantidades y presentan una serie de propiedades. 

El conocimiento de estas operaciones y de sus propiedades es muy importante para el desarrollo de la lógica de programación, ya que se concretan como operadores (especialmente como operadores algebraicos) en los lenguajes de programación.

miércoles, 3 de agosto de 2022

OOo Basic. Datos.

Lenguajes de programación. Variables como componentes básicos.

Todos los lenguajes de programación cuentan con estos componentes: datos, variables (y constantes) y operadores, aunque pueden diferir en la variedad de tipología y en la cantidad de elementos. Por ello es importante entender qué son y qué función cumplen. Empezaremos por algunas definiciones y por cómo se concretan en PSeInt en cuanto lenguaje, pero sobre todo por su carácter de pseudocódigo.




Existen varios significados para el término dato, así que empezaré por exponer cómo se define en el diccionario de la RAE.

Dice la RAE que... es una palabra que procede del latín datum, "lo que se da" y lo define como "Información sobre algo concreto que permite su conocimiento exacto o sirve para deducir las consecuencias derivadas de un hecho". También lo considera sinónimo de "fundamento" e incluso aporta una definición en el campo de la informática: "Información dispuesta de manera adecuada para su tratamiento por una computadora". Es significativo que la RAE aporte una definición específica dentro del ámbito de la informática, cosa que no sucede con otros términos también de uso frecuente en este campo.

Por mi parte planteo dos formas de entender un dato:
  • Como el contenido con el que trabaja un algoritmo para resolver un problema. 
  • Pero también como elemento de un conjunto (que puede ser unitario o estar formado por varios elementos), por lo que resulta ser elemento perteneciente al conjunto de las parte de un conjunto de un conjunto P(X).
La primera definición está necesariamente incompleta, por lo que la modificaré uy ampliaré más adelante. La segunda nos permite formalizar el análisis de un algoritmo y de los datos con los que éste trabaja, en términos de lógica matemática y teoría de conjuntos.

Los tipos básicos de datos son tres, aunque cada lenguaje de programación aporta subdivisiones que amplían este número, especialmente en cuanto a los tipos numéricos, como veremos en su momento:
  • Numéricos
  • Alfanuméricos
  • Y lógicos
Los lenguajes de programación, por lo general, cuenta con los siguientes tipos de datos:
  • Numéricos:
    • Enteros
    • Reales
  • Alfanuméricos
    • Carácter
    • Cadena
  • Lógicos
La distinción entre enteros y reales es importante, ya que permite diferenciar entre los números del conjunto Z del resto de los números del conjunto R. A parte de la relevancia lógico-matemática que esto implica, esta diferenciación también tiene que ver con la gestión de la memoria RAM, por los motivos que veremos en su momento.

Los datos alfanuméricos, son en realidad un único tipo de datos que podemos declarar indistintamente como Carácter o como Cadena.

Finalmente los datos lógicos, también llamados booleanos, son los que adoptan como resultado dos valores lógicos: Verdadero o Falso. A pesar de su sencillez aparente, estos datos son de gran importancia para el desarrollo de un algoritmo, ya que permite desplegar toda la potencia de la lógica matemática.

Frente a la claridad terminológica con que el diccionario de la RAE aborda el concepto de dato, el de variable resulta insatisfactorio al no contemplar el significado principal que tiene en el ámbito de la programación. De hecho puede acentuar la confusión que existe al respecto, ya que refuerza la idea de variabilidad y cambio. Incluso el concepto matemático de variable incide en este rasgo ("Magnitud que puede tener un valor cualquiera de los comprendidos en un conjunto"), que si bien es pertinente, no es sustancial para la lógica de programación.

Para comprender la lógica de programación en lo tocante a las variables, es necesario destacar que una variable es, en realidad, un nombre o indicador de un espacio de la memoria RAM, una especie de identificador del casillero dentro de ésta, que es el espacio en el que se almacenan los datos.

Esto me lleva a ampliar mi primera definición de dato que ya señalé como provisional e incompleta: desde esta perspectiva, un dato es un contenido almacenado o adscrito en un espacio concreto de la memoria RAM al que se accede mediante un identificador llamado variable. Los datos son los contenidos con los que opera un algoritmo para resolver un problema.

Desde esta perspectiva no existe una diferencia sustancial entre variable y constante, ya que en cuanto a ser identificador de un espacio de memoria, nada las diferencia; tampoco el hecho de contener datos, pero si de lo que se trata es de comprender la diferencia funcional entre ambas, su distinción sí es pertinente y el significado matemático de variable se ajusta perfectamente a esta distinción: mientras que una variable puede cambiar de contenido a lo largo de un algoritmo, una constante permanece invariable, siendo ésta su característica específica y diferencial.

No obstante, la importancia de la dimensión "física" que subyace a una  variable en cuanto a identificador de un espacio de memoria es fundamental para entender su relación con el contenido que almacena. Y es que entre dato y variable existe una determinación recíproca (o inter-determinación, si se me permite) que se expresa específicamente en el ámbito del análisis tipológico: todo cuanto antes dijimos sobre los tipos de datos es igualmente válido en cuanto a tipos de variable.

El trabajo que conlleva la correcta identificación de las variables de un programa y qué hacer con ellas es, en buena medida, uno de los trabajos principales del programador o programadora, siendo necesario realizar varias operaciones:
  • Definir la o las variables
  • Declararlas (o asignar tipo -> tipificarlas)
  • Asignar los datos a las variables
  • Y operar con ellas para obtener nuevos datos que, a su vez, se asignan a otras variables.
Algunos lenguajes de programación son más flexibles que otros en lo tocante a la definición y declaración de las variables, lo que personalmente no considero ninguna ventaja por los errores que esto puede producir, además de ocultar, hasta cierto punto, las restricciones recíprocas que son consustanciales con la inter-determinación antes señalada entre datos y variables. No obstante, todos los lenguajes guardan coherencia con este principio básico: no se pueden mezclar churras con merinas... salvo que estemos hablando de ovejas en general y la distinción de razas (tipos) sea irrelevante, algo que no suele ocurrir en programación y que, en ese caso, se encuentra sometido a otra lógica.

Cuando es prescriptivo definir o declarar una variable, es un proceso que se resuelve con una única expresión pero encubre, en realidad, tres procesos que están, eso sí, íntimamente relacionados:
  • Declarar o denominar (dar nombre)  la variable
  • Determinar o definir su tipología.
  • Dimensionar y ajustar el espacio de memoria RAM denominada/identificada mediante el nombre de la variable.
Aunque lo común es denominar estos tres procesos con un único término, éste puede cambiar en función de en qué parte del proceso se ponga el acento: mientras que en PSeInt se hace sobre la definición tipológica (Definir var1 Como Tipo), en los lenguajes Basic se usa la expresión Dim (dimensión/dimensionar), que mantiene al uso inicial dentro de este lenguaje en referencia al establecimiento de las dimensiones de una matriz de datos (o array), que se traslada al manejo de variables. Pero otra denominación de este proceso es declarar, que será la que utilizaré en este blog.

Con independencia del interés de esta cuestión, que no es únicamente por lo que hemos visto, hay otras de mayor transcendencia práctica. Aunque por ahora no las abordaré todas, sí me interesa tratar una de ellas: es necesario saber si el lenguaje de programación por el que optamos exige o no declarar las variables y las constantes que se van a emplear, si da opción a hacerlo explícitamente (como es el caso de OOo Basic) o si permite o impone de alguna manera la "declaración implícita". Este último es el caso de Python: en este lenguaje no existe declaración específica, sino que se realiza implícitamente en el momento de realizar la asignación de datos a las variables.

Y al entrar en este tema, nos enfrentamos necesariamente con el concepto de operador, que podemos definir como el elemento del lenguaje de programación que permite relacionar variables y concretar qué hacer con su contenido. 

Ya hemos visto uno de los operadores de mayor frecuencia de uso: el operador de asignación. Más abajo vamos a volver sobre él, pero ahora quiero resaltar que el modo en que se expresan las instrucciones de asignación refleja la naturaleza de una variable: ser etiqueta identificadora de su contenido (el dato) que se almacena en un espacio de memoria. En este último sentido, la variable es el puntero que señala la ubicación del espacio de memoria.

Existen también distintos tipos de operadores, así como tipologías diferentes según autores. Algunas son más amplias y otras restringidas. En el primer caso (que es el que empleo) se habla de los siguientes tipos:
  • De asignación
  • De concatenación
  • Algebraicos
  • Relacionales
  • Y lógicos.
Algunos operadores está restringidos a determinados tipos de variables, pero otros son de carácter universal (se usan con los diferentes tipos de variables). De momento vamos a recordar el de asignación, que pertenece al primer grupo. Y digo recordar porque ya te hablé de él. El resto serán explicados más adelante, en relación con la exposición sobre los tipos de datos/variables y sus características.

El operador de asignación se representa en OOo Basic como = y la expresión resultante se lee de derecha a izquierda: 
  • NombreVariable <- DatoX se lee: el dato X está asignado a la variable NombreVariable, lo que permite al algoritmo utilizar el nombre de la variable (en lugar del valor asignado a ésta) en la formulación del resto de las instrucciones en que dicho dato sea empleado.
  • No obstante, aunque <- es la representación universal, el signo más empleado en los lenguajes de programación es el signo =
Desafortunadamente dicho signo da lugar a confusión terminológica, ya que coincide con el signo "igual" de las operaciones matemáticas, cuando en realidad lo que estamos haciendo es únicamente una asignación, que viene a equivaler a una especie de "igualación" lógica, que no matemática. Por ejemplo, si a la variable Nombre asignamos el dato "Pedro" (Nombre = "Pedro"), lo que decimos es que Pedro es el dato que contiene (que tiene asignado) la variable Nombre (pudiendo tener asignado cualquier otro) y que podemos utilizar la referencia Nombre siempre que deseemos emplear su dato asociado. Este significado es igualmente válido cuando asignamos datos numéricos a variables numéricas.

La asignación también permite relacionar una variable con el resultado de una operación entre variables, que es la segunda fuente de datos de un algoritmo. En este caso, el dato asignado a dicha variable es el resultado de esa operación. Y la forma en que esta asignación se realiza es reveladora del significado real de dicha operación (asignación).

Por ejemplo, si asignamos a la variable ResultadoSuma el resultado de la suma de los datos x y (ResultadoSuma = x + y), no lo hacemos mediante su expresión algebraica x+y=ResultadoSuma. Lo mismo sirve cuando empleamos los nombres de la variables en sustitución de los datos que éstas contienen (ResultadoSuma = SumandoX + SumandoY).