viernes, 30 de septiembre de 2022

Introducción. Lenguajes.

Colecciones de datos

Hasta el momento únicamente disponemos de variables (y constantes) como recurso para almacenar información, bueno, para referenciar la ubicación de los datos en la memoria RAM, para ser más precisos. Pero una variable sólo permite almacenar un dato cuando ya hemos podido comprobar que en un algoritmo muchas veces necesitamos almacenar muchos. Es cierto que según qué circunstancias multiplicar el número de variables es la única solución, pero en otras muchas los arreglos, matrices o vectores (que diferentes nombres tienen) son una buena alternativa. En esta entrada vamos a trabajar sobre los arreglos en PSeInt, paso previo para su estudio en Python. 

En la propia documentación de PSeInt, a la que puedes acceder desde este comando...


... puedes encontrar información sobre los arreglos, también identificados como vectores o matrices: los arreglos se definen como estructuras homogéneas de datos que permiten almacenar un determinado número de elementos.

Si pensamos en términos de teoría de conjuntos, podríamos definir un arreglo como un conjunto o subconjunto no unitario, en oposición a una variable que se definiría como conjunto o conjunto unitario.

Es importante entender que un arreglo requiere que los datos sean homogéneos, esto es: que todos sean del mismo tipo. En principio, en un arreglo no podemos mezclar datos alfanuméricos con datos numéricos y/o lógicos. De ahí que dijera antes que podremos utilizar arreglos en lugar de variables en determinadas circunstancias, esto es: cuando los datos que deseamos almacenar en la memoria RAM sean homogéneos.

Para trabajar con arreglos hay que aprender a crearlos, pero necesitamos un segundo paso: necesitamos dimensionarlos también, cosa que no es necesario hacer con las variables ya que, por definición, éstas sólo hacen referencia a un datos (son conjuntos unitarios, como ya dije).

Definir o declarar un arreglo no presenta ninguna diferencia con declarar una variable; de hecho se pueden declarar o definir variables y arreglos en una misma instrucción (siempre, claro está, que ambos sean del mismo tipo). Por ejemplo: Definir palabra, palabras como caracter; es perfectamente válido: palabra es una variable y palabras es un arreglo. Bueno, aun no lo es, ya que para que lo sea necesitamos dimensionarlo.

De hecho, mientras que a la variable palabra queremos asignarle un datos, podemos hacerlo directamente mediante un operador de asignación (palabra<-"Casa";), pero si queremos hacer lo mismo con el arreglo palabras (palabras<-"casa","mesa"...;) PSeInt nos manda un aviso de que hemos introducido demasiados parámetros, que viene a ser lo mismo que decir que palabras está siendo entendido como variable (y a una variable sólo se le puede "asignar" un dato o elemento).

Para evitar este problema, primero deberemos dimensionar el arreglo mediante la instrucción Dimension: Dimension palabras[3], por ejemplo, significa que:

  • palabras no es una variable (alfanumérica), es un arreglo (alfanumérico)
  • el arreglo palabras[n] hace referencia a una dirección en la memoria RAM que contiene una lista de elementos (homogéneos) formada por n número de datos o elementos.
  • nuestro arreglo tiene un máximo de 3 elementos, siendo este dato delimitador de los índices que las identifican posiciones que ocupan en la memoria RAM.
Claro que podemos decir que palabras[] está formada por 1 elemento, con lo que sería equivalente a la variable palabra del ejemplo. Hacer esto carece de sentido, pero no el objetivo que subyace: reducir el impacto de la declaración de matrices en la disponibilidad de memoria RAM del sistema. Para evitar los problemas de dimensionar un arreglo por demás o por de menos cuando no sabemos a priori cual sería su dimensión, lo que podemos hacer es establecer una variable numérica de tipo entero como valor de la dimensión...

      Definir palabras Como Caracter;
Definir i como entero;
leer i;
Dimension palabras[i];

... de este modo estableceremos la dimensión del arreglo de forma interactiva (en tiempo de ejecución), lo que da mucha más flexibilidad al algoritmo, aunque presenta un problema: no podremos modificar esta dimensión a lo largo de todo el algoritmo... en PSeInt.

La forma de asignar datos a un arreglo es muy simple: indicamos el nombre, la posición del dato en el arreglo (o índice) y, utilizando el operador de asignación, procedemos como en una variable:

palabras[0]="casa"; asigna "casa" a la primera posición del arreglo palabras[]. Y así con los siguientes datos del arreglo hasta finalizar la lista de elementos de su índice.

NOTA. Esta primera posición se identifica, por defecto, con el valor 0, aunque podemos cambiar la configuración de PSeInt para que el conteo de los índices empiece en 1, cosa que no recomiendo por mantener la similitud con los lenguajes de programación, que normalmente utilizan el criterio de inicio 0.

Para acceder al contenido del arreglo, y más concretamente a un dato concreto, deberemos utilizar el nombre del arreglo seguido del índice asociado a ese dato entre []: Escribir palabras[0] nos devuelve Casa.

También podemos permitir al usuario o usuaria introducir datos en un arreglo, además de ajustar sus dimensiones (número de datos que contiene). Esta segunda opcionalidad la vimos antes y consistía en utilizar una variable numérica (entero) como valor de la dimensión. La primera requiere el uso de un bucle. Un ejemplo:

Proceso Arreglo2
//Declarar arreglo y variables para contador e índice
Definir palabras como caracter;
Definir i, contador como entero;
//Dar valor al número de elementos del arreglo
Escribir "¿De cuántas palabras va a estar compuesta esta lista?";
Leer i;
//Dimensionar el arreglo
Dimension palabras[i];
//Solicitar valores para incluir en el arreglo
Para contador = 0 Hasta i-1 Hacer
Leer palabras[contador];
FinPara
//Devolver información a demanda del usuario o usuaria
Escribir "¿Qué datos quieres ver?. Indica la posición que ocupa en la lista.";
leer i;
i = i-1;
Escribir palabras[i];
FinProceso

Aunque me ahorro comentarios que ya están incorporados al código, sí me parece necesario explicar el modo de usar el bucle Para y la forma en que se maneja la variable i.
  • El correcto funcionamiento del bucle requiere de dos variables: una con función de contador (contador) y otra como límite de iteraciones (i). Esta segunda debe ser igual al total de elementos del arreglo, por lo que utilizamos la expresión i-1, ya que i, tal y como queda definida antes sobrepasa en 1 el número de elementos del arreglo (por eso de que se inicia en 0), lo que generaría desbordamiento: el número de iteraciones superar en 1 al número de elementos del arreglo "dimensionados" mediante i. En consecuencia, lo correcto es Para contador = 0 Hasta i-1 Hacer.
  • También utilizamos el valor del contador contador para identificar el índice del arreglo en el que introducir el dato durante las iteraciones, ya que es contador la variable que se ajusta a la iteración, puesto que es la variable asociada a Para. Si usáramos la variable i, cuyo valor establecimos antes (Leer i;) como número de elementos del arreglo, ya en la primera iteración nos situaríamos fuera de sus límites.
  • Las mismas razones por las que en el bucle establecimos como límite de iteraciones i-1 nos llevan en la solicitud de datos a asignar a i el valor i+1 para acceder a una posición del arreglo: i = i-1permite que el usuario o usuaria no tenga que estar pensando en términos de programador y, claro está, no sería lógico explicar que debe descontar 1 al número que introduzca para obtener el dato deseado.
Para finalizar esta iniciación al uso de arreglos (colecciones de datos), se me ocurre que podemos crear un algoritmo que simule una sencilla base datos con tres campos en la que el usuario o usuaria pueda introducir el número de registros que desee y complete los tres campos de cada uno de los registros; posteriormente deberá poder realizar una consulta utilizando el nombre del alumno o alumna como criterio de búsqueda, mostrando el programa en pantalla la información obtenida. Esta es mi versión, te la dejo para que la analices.

miércoles, 28 de septiembre de 2022

Python. Estructuras.

Bucle no determinista en Python

Python cuenta también con un bucle no determinista, si bien su sintaxis presenta las características propias de este lenguaje. En esta entrada estudiaremos su lógica y algunas de sus características formales.


Al final de la entrada anterior definimos, de forma más intuitiva que formal, el marco de la lógica matemático-proposicional en que se sustenta el funcionamiento de un bucle no determinista. La actividad de ejemplo, que es traslación de la que ideamos en la entrada relativa a la lógica de programación, nos permitió plantear, también de forma simple, al menos una de las funcionalidades de este tipo de bucle.

En esta entrada analizaremos cómo se expresa y cómo funciona un bucle no determinista en Python y lo ejemplificaré con el mismo algoritmo, pero también pretendo desarrollar algún otro para mostrar alguna utilidad más. De este modo vamos ampliando el abanico de posibilidades que nos ofrecen las estructuras básicas de los lenguajes de programación, algo que nunca viene mal a nuestro propósito.

Ya sabemos que un bucle no determinista se caracteriza por el desconocimiento que tenemos a priori del número de iteraciones necesarias para el cumplimiento de la condición de salida del bucle. 

En esto, en Python el bucle no determinista while se ajusta más al funcionamiento y filosofía de este tipo de bucle tal y como expusimos en el análisis del mismo en las dos entradas anteriores, así que no nos vamos a encontrar con las diferencias que respecto a estas bases sí observamos en el bucle determinista (for).

Tampoco presenta las diferencias sintácticas y las variaciones de funcionamiento que presenta el bucle do...loop de OOoBasic respecto al modelo PSeInt. No obstante también observaremos algunas diferencias respecto a este modelo, lo que justifica la necesidad de esta entrada.

La primera diferencia es consecuencia de la sintaxis abreviada de Python: en este lenguaje while no tiene sentencia que marca el final del bucle y el uso del sangrado no es mera estética. Al igual que en el resto de las estructuras estudiadas hasta el momento (condicional if y bucle for), el sangrado o indentación tiene una función sintáctica fundamental: los errores que pudiéramos cometer al respecto impiden el correcto funcionamiento del bucle o implican errores de interpretación del mismo. En ambos casos, como mínimo, afectan al desarrollo del algoritmo.

Veamos la comparación entre el bucle while en PSeInt y en Python en lo relativo al bucle while:


  • Python inicia el bucle con la sentencia while seguida de la proposición a valorar: en este caso esta proposición está formada por la relación de no igualdad entre dos variables (se emplea el operador de diferencia).
  • Esta línea finaliza obligatoriamente con dos puntos (:)
  • Python carece de comando de cierre del bucle. 
  • Este cierre se define por el fin del sangrado. 
  • Si la última línea estuviera sangrada también, sería incluida en la iteración. 
  • Si la última línea del bucle no estuviera sangrada, sería excluida y (en este caso) se generaría un bucle infinito.
  • La lógica de funcionamiento del bucle es la misma que en PSeInt: el bucle continua la iteración mientras la condición NumIngresado!=NumSecreto siga siendo V y finaliza cuando sea F.
NOTA. Dado que input genera un dato tipo alfanumérico, una solución es emplear este mismo tipo de dato en la asignación de contenido de la variable NumSecreto, ya que, en caso contrario, como el número 5 no es igual al caracter alfanumérico "5", el bucle se torna infinito. Otra solución es realizar la conversión de alfanumérico a tipo entero mediante la función específica. Optamos por la primera solución por no haber tratado aun el tema de las funciones en Python. 

Te dejo a continuación el código Python para que lo puedas copiar, ya que lo anterior es una imagen. Después de lo explicado antes, no veo necesario incorporar ningún comentario:

NumSecreto = "5"
NumIngresado = input("Dime un número del 0 al 20: ")

while NumIngresado!=NumSecreto:
    print("LO SIENTO, NO ES EL NÚMERO",end="\n")
,     print("Vuelve a intentarlo",end="\n")
    NumIngresado = input("Dime otro número (0-20): ")

print("Exacto! Has adivinado el número. Era el 5")

Como uso posible de este algoritmo, aunque lógicamente más complejo, piensa, por ejemplo, en una batería de ejercicios (problemas matemáticos simples, de una única operación) en los que sucesivamente planteáramos al alumnado el texto del problema y, mediante un bucle while solicitáramos la respuesta. Además, si añadiéramos una variable para almacenar la puntuación, junto con un condicional, generaríamos un algoritmo que nos serviría para aplicar pruebas del tipo de WSIC-Aritmética. Otro ejemplo podría ser una prueba de adivinanzas, como la de WPSSI-III o la de RIAS.

Como estos ejemplos requieren tiempo, es preferible apuntarlos como posibles propuestas de trabajo, pero es posible desarrollarlas ahora. Lo que sí podemos hacer es pensar otro tipo de problemas para los que un bucle while podría ser una buena solución.

Un ejemplo que se me ocurre es un sistema de control de acceso mediante clave, claro que es posible que un bucle for resultara más apropiado, si queremos controlar el número de veces que se permite repetir la entrada de clave; pero como no es este nuestro caso, while nos puede venir muy bien. este sería el código:

#Control de usuario

print("Te damos la bienvenida a nuestro sistema de acceso",end="\n")

print ("Introduce, por favor tu nombre, después tus apellidos y finalmente tu clave",end="\n")

vNombre = input("Nombre: ")

vApellidos =input("Apellidos: ")

vClave = input("Clave: ")

vClaveValida = "MiClave"

while vClave !=vClaveValida:

    print("Lo siento ",vNombre,", esa no es tu clave",end="\n")

    vClave = input("Introduce de nuevo tu clave: ")

print("*************************************************")

print("Datos correctos", end="\n")

print("Se permite el acceso de", vNombre,vApellidos,"al sistema",end="\n")

print("*************************************************")

NOTA:  He asignado a la variable vClaveValida un valor ficticio ("MiClave") que podrás sustituir por el que tu desees.

Este ejemplo no deja de ser un mero ejercicio de entrenamiento en el uso de while, así que no pienses que el control de acceso mediante clave es algo tan simple. No obstante tiene algo de interés, y es que, en esencia, es de lo que se trata, aunque mucho más sofisticado.

Una de las mejoras posibles es controlar el número de veces que se permite al usuario o usuaria fallar a la hora de introducir la clave, ya que de no hacerlo habrás intuido que nuestro bucle acabará siendo un bucle infinito. Para evitarlo es necesario controlar cuantas iteraciones permitimos, y para ello necesitamos dos cosas: un contador y una forma de alterar el funcionamiento del bucle.

#Control de acceso con limitación de intentos

print("Te damos la bienvenida a nuestro sistema de acceso",end="\n")

print ("Introduce, por favor tu nombre, después tus apellidos y finalmente tu clave",end="\n")

vNombre = input("Nombre: ")

vApellidos =input("Apellidos: ")

vClave = input("Clave: ")

vClaveValida = "MiClave"

i = 0

while vClave !=vClaveValida:

    i+=1

    if i==3:

        break

    print("Lo siento ",vNombre,", esa no es tu clave",end="\n")

    vClave = input("Introduce de nuevo tu clave: ")

print("*************************************************")

if vClave ==vClaveValida:

    print("Datos correctos", end="\n")

    print("Se permite el acceso de", vNombre,vApellidos,"al sistema",end="\n")

else:

    print("Lo siento",vNombre,", tu sesión ha caducado",end="\n")

    print("Vuelve a intentarlo")

print("*************************************************")

  • Al incorporar la variable contador i y utilizarla de modo incremental ( i+=1) dentro del bucle, limitamos el número de intentos al valor especificado como tope en el condicional que incluimos en el cuerpo del bucle ( if i==3:).
  • La sentencia break, asociada al condicional nos permite cerrar la iteración cuando el contador alcance el valor establecido (3). De este modo evitamos que el bucle se torne infinito.
  • El código subsiguiente reproduce una estructura condicional, ya que no es lo mismo que salgamos de bucle por haber escrito correctamente la clave que sea por haber agotado el número de oportunidades.
Evidentemente, tanto en este algoritmo como en el anterior, falta lo importante: el contenido al que accedemos, pero esta no era ahora la cuestión. Si lo son estas tres cuestiones:

  • El uso combinado de bucles y condicionales.
  • El uso sintáctico de los niveles de sangrado como limitadores del cuerpo de ambos (bucle y condicional)
  • Y el uso de la sentencia break, la cual, junto con continue, nos permiten alterar el funcionamiento de los bucles, dándonos mayor flexibilidad a la hora de trabajar con estas estructuras.
Por el momento, damos por finalizada esta entrada. Queda de tu parte practicar y practicar... y pensar en posibles usos de las posibilidades que nos ofrece while y la combinación de bucles y condicionales.

domingo, 25 de septiembre de 2022

Introducción. Lenguajes.

 PSeint. Iteración. Bucle no determinista

Esegundo tipo de bucle es el denominado no determinista o indeterminista. Se diferencia del anterior en que, en este caso no se conoce a priori el número de iteraciones o ciclos que son necesarios para alcanzar el objetivo.


Un bucle no determinista se repite hasta que se alcanza o cumple determina condición, que es conocida a priori, pero ignoramos las veces que es necesario repetir el bucle hasta que la condición se cumpla. 

En términos de lógica proposicional esto supone que la proposición que rige el bucle es falsa y llegado un momento se torna verdadera: este es el momento en que finaliza el bucle.

En PSeInt, y en general, en lógica de programación, se invierten los términos del valor de verdad de la proposición, de modo que es verdadera al inicio del bucle y éste finaliza cuando pasa a ser falsa. De este modo, el bucle puede no llegar a ejecutarse si la condición a la que responde es verdadera desde el inicio.

El modo de traducir esto a pseudocódigo es mediante la expresión Mientras, que en los lenguajes de programación se traduce por While.

PSeInt lo expresa del siguiente modo:

Mientras ExpresionLogica Hacer

secuencia_de_acciones

Fin Mientras

Se evalúa la expresión lógica y si es verdadera, y sólo entonces, se ejecuta la secuencia de acciones que esta delimitada en el cuerpo del bucle. Pero si esta condición no se da (si ExpresionLogica-> F), entonces no se desarrolla la secuencia de acciones prevista para cuando ExpresionLogica->V.

La representación de la estructura Mientras mediante flujograma es la siguiente:


Para poner en práctica estos contenidos vamos a crear un juego de adivinanzas: se trata de adivinar un número que previamente hemos establecido. El usuario o usuaria deberá ingresar un número y si acierta finaliza el bucle, pero si falla continua abierto, dando así (en teoría) infinitas oportunidades de acertarlo.

Este algoritmo es una simplificación del que PSeInt proporciona como ejemplo. El original puedes verlo si desde el comando Ayuda...


... solicitas información sobre el comando MientrasMi algoritmos es mucho más simple. Te lo muestro y explico:

Proceso AdivinaNumero1

Definir NumSecreto,NumIngresado Como Entero;
NumSecreto <- 5;
Escribir 'Adivina el numero que he escrito: está entre el 0 y el 20):';
Leer NumIngresado;

Mientras NumSecreto<>NumIngresado Hacer
Escribir 'LO SIENTO, NO ES EL NÚMERO';
Escribir 'Vuelve a intentarlo';
Leer NumIngresado;
FinMientras

Escribir 'Exacto! Has adivinado el número. Era el 5';

FinProceso

Este sencillo algoritmo se puede convertir en una pesadilla si nos olvidamos de un "pequeño" detalle. De hecho, sin esa sencilla línea de código entraríamos en un bucle infinito, lo que resulta muy fastidioso. Este peligro no se da en los bucles finitos, ya que controlamos el número de ciclos, pero es mucho más frecuente de lo que se podría pensar en los bucles no deterministas.

Si te fijas, hay una línea de código que se repite dos veces: una antes de iniciar el bucle y otra dentro del bucle: Leer NumIngresado. Esta repetición no es un error ni tampoco una redundancia: es fundamental para que el bucle no se repita indefinidamente:
  • Antes del bucle genera el dato necesario para que sea posible valorar si la proposición NumSecreto<>NumIngresado es V. Si lo es, entonces se desarrolla el bucle, pero si es F, se sale del bucle y se procesa la línea final (Escribir 'Exacto! Has adivinado el número. Era el 5';)
  • Dentro del bucle, formado parte del conjunto de sentencias que conforman el cuerpo del bucle, permite que el usuario vuelva a introducir un número, que a su vez permite modificar las condiciones iniciales de la proposición  NumSecreto<>NumIngresado. Si esta línea no estuviese donde está, dicha proposición siempre sería V, con lo que el bucle se repetiría indefinidamente.
Para que puedas ver mejor cómo se desarrolla este algoritmo, PSeInt dispone de una funcionalidad muy interesante: Ejecutar paso a paso


Te recomiendo que la actives, mejor aun habiendo activado previamente el visionado del flujograma...

... y que juegues un poco con este sencillo juego. Precisamente te dejo abajo su flujograma para que dispongas de toda la información.


Con esto finalizo la entrada actual. En la siguiente trabajaremos con los bucles no deterministas en OOo Basic. Podrás comprobar que presentan diferencias respecto a la lógica de programación empleada en la actual.

viernes, 23 de septiembre de 2022

Python. Estructuras.

Bucle determinista en Python

Toca ahora aprender ahora la sintaxis del bucle For en Python, la cual presenta características especiales y diferenciadas en este lenguaje. De hecho, trabajar con un bucle For en Python presupone cierto grado de conocimiento de lo que son los conjuntos de datos y las funciones, contenidos éstos sobre los que aun no hemos trabajado, por lo que implica alterar en cierto modo el orden lógico de aprendizaje de este lenguaje. Aun así, por mantener la coherencia con la temática, será conveniente iniciarse en el manejo de este componente del lenguaje.


Lo primero que tenemos que entender para comprender el funcionamiento de bucle For en Python es que no es exactamente igual a cómo se plantea en otros lenguajes, incluyendo la estructura genérica que hemos visto en Pse Int, aunque una de las dos formulaciones obedece claramente a esta lógica.

Efectivamente, en Python contamos con dos formulaciones básicas del bucle For:
  • La que se ejecuta sobre un elemento iterable como es un conjunto o colección de datos, como una lista, una tupla, un diccionario o una cadena de texto.
  • Y la que sirve para realizar una iteración o repetición cíclica definida en función de unos datos específicos de inicio, finalización y paso.
Veremos con cierto detalle ambas formulaciones, pero en función de lo dicho antes podemos concluir que la segunda de esas formulaciones es la que tiene más parecido lógico-funcional con lo que hasta el momento sabemos sobre el bucle For, resultado la segunda predefinida por ese elemento iterable.

Empezaré entonces por la segunda formulación, y veremos que la supuesta similitud es funcional, pero que en Python se rige por su propia lógica. Me refiero a la instrucción que se construye del siguiente modo:

for vcont in range(1,11,2):
  • vcont es una variable a la que damos la función de contador.
  • range(1,11,1) es una función (en realidad los expertos dicen que es una clase) que puede formularse como en este ejemplo, esto es: con tres valores numéricos separados por comas. En caso de escribirse así, el primer valor (1) identifica al valor de inicio del recorrido, el segundo (11) el valor final (también hace función de número de iteraciones o ciclos en los ciclos crecientes) y el tercero (2) el paso o salto de ciclo en ciclo. En su formulación más simple (range(11)) únicamente se especifica el número de ciclos o iteraciones, estado implícitos valores de inicio (0) y el salto (1). El de inicio se considera 0, puesto que 0 es el valor inicial de toda matriz en Python.
Veamos mejor un ejemplo para entender cómo funciona este ciclo:

        for vCont in range(1,11,1):
            print(vCont)

    Respuesta:
1
2
3
4
5
6
7
8
9
10
  • El inicio del bucle queda indicado por la sentencia For, su final con la eliminación de la sangría que se aprecia en la segunda línea (print(vCont))
  • Como ya dije respecto al condicional if, en Python la sangría no es únicamente un formalismo para facilitar la lectura del código, es parte de la sintaxis del lenguaje.
  • Como respuesta obtenemos un listado de números que finaliza en 10, y no en 11 como debería ser atendiendo al segundo valor de range(). Esto es así porque toda matriz en Python se inicia en 0. Nosotros al indicar que el recorrido comience en 1 estamos anulando la escritura de dicho valor inicial.
  • En consecuencia range(1,11,1) no es estrictamente equivalente a range(11), ya que el resultado de la segunda formulación incluye 0 como valor de inicio, mientras que en la primera formulación indicamos que ese valor sea 1. Lo que sí se mantiene es que el salto es de 1 en 1 (el tercer valor se supone implícitamente como 1) y el final es 10, por la misma razón que expuse antes.
  • También podemos escribir dos parámetros en la función range(), pero serán siempre el valor de inicio y el número de ciclos, si deseamos que esté incluido el parámetro paso estamos obligados a escribir los tres, ya que Python interpreta los dos primeros como valor de inicio y número de ciclos.
  • Estos valores tienen que ser siempre números enteros, no decimales.
  • Es posible plantear ciclos decrecientes, siendo obligado entonces que el valor de inicio sea superior al de final y que el paso sea un entero negativo.
for i in range(11,1,-1):
  • También podemos escribir en la sentencia print() el contenido de deseemos; el uso de la variable contador en el código anterior es meramente didáctico. Por ejemplo, podemos repetir un texto x veces o el contenido de una variable a la que previamente hemos asignado una cadena de caracteres.
vtexto="Hola personal"
for i in range(5):
    print(i)
    print(vtexto)

Respuestas:
    
0
Hola personal
1
Hola personal
2
Hola personal
3
Hola personal
4
Hola personal

La segunda formulación de este bucle es como medio para recorrer un elemento iterable, como es el caso de una lista o de una cadena de texto. Dado que aun no hemos hablado de los conjuntos de datos (una lista es un tipo de conjuntos de datos), me limitaré a utilizar for como "iterador" en su formulación más simple.

for i in (1,2,3):
    print(i)

Respuesta:
1
2
3

Pero también... 

for txt in("fresa","manzana","pera"):

    print(txt)

Respuesta:

fresa
manzana
pera

  •  En este caso la variable (i / txt) queda asociada a la lista de elementos que se definen entre paréntesis tras in()
  • Y lo que hace el bucle es recorrer esta lista y (mediante print()) escribirla en pantalla.
  • Este forma permite, con alguna variación, sustituir esta lista final de sentencia por el nombre de una colección de datos o de una variable de cadena (alfanumérica) que hayan sido previamente declaradas. Por ejemplo:

 var = "pelota"

for i in var:

    print(i)

Respuesta:

p
e
l
o
t
a

En este caso la lista que antes seguía a in es ahora la variable var. El ciclo recorre var asignando en cada ciclo cada uno de sus componentes (caracteres en este caso) a  la variable contador i. Mediante print() mostramos esos componentes por pantalla.

Son muchas las cosas que se pueden decir y hacer con For, pero para que se entiendan mejor es necesario trabajar antes con colecciones de datos, así que prefiero tratarlas más a fondo cuando trabajemos con esos elementos del lenguaje, por lo que queda pendiente para entonces. De momento no sería mala idea entrenar la sentencia For según las formas de uso expuestas en esta entrada.

domingo, 18 de septiembre de 2022

Introducción. Lenguajes

PSeint. Iteración. Bucle determinista

El bucle para es el representante del tipo de bucle o ciclo que denominamos bucles deterministas en la entrada anterior. Vamos a dedicar esta entrada a trabajar con este bucle desde la perspectiva de la lógica de programación utilizando PSeInt como herramienta.


Definimos en ese momento los bucles deterministas como aquellas estructuras que facilitan la iteración en las que se conoce de antemano el número de ciclos que va a realizar el bucle. La estructura que representa este tipo de estructuras es, por antonomasia, el bucle Para (For en los lenguajes de programación).

En PSeInt esta estructura presenta la siguiente sintaxis:

        Para <variable> <- <Valor inicial> Hasta <Valor final> (Con Paso <paso>) Hacer 
            <instrucciones> 
        FinPara

Su lógica de funcionamiento es relativamente simple: 
  • Mientras se cumpla la condición F en la proposición Variable es igual a Valor, siendo F mientras Valor no supere Valor final (en ese caso el valor lógico es  V) se hará (ejecutará) la instrucción o instrucciones situada(s) entre el inicio de la estructura (Para) y su fin (FinPara)
  • La instrucción complementaria (por eso la escribo entre paréntesis ()) no es obligatoria cuando el Paso o incremento de la variable es incremental +1, pero sí es necesaria cuando queremos que el incremento sea diferente de 1.
  • El valor inicial no tiene por qué ser necesariamente mayor que el final, aunque es lo más frecuente (Valor inicial > valor final). En este caso hablamos de bucle creciente. También puede ser decreciente (Valor inicial Valor final). En este caso es necesario explicitar Paso dando un valor negativo al número <paso>.
Es posible utilizar una formulación más transparente de esta estructura en PSeInt, aunque para ello deberemos habilitar esta opción desde Opciones del lenguaje (Menú Configurar). De indicarlo podremos escribir la orden o sentencia inicial como...
Para <variable> Desde <inicial> Hasta <final> Con Paso <paso> Hacer

... aunque opino que no resulta necesario, ya que la opción por defecto es suficientemente clara.

Aunque los usos del bucle Para son varios, uno de los que se utilizan con más frecuencia para ejemplificar esta estructura es la construcción de la tabla de multiplicar de un número. Por ello vamos a crear un algoritmo en PSeInt que genere la tabla de multiplicar de un número solicitado al usuario o usuaria y aprovecharemos este ejemplo para mostrar cómo se representa Para mediante un flujograma.

Este estas son las especificaciones y a continuación te muestro el código:

Crear un programa que solicite al usuario o usuaria el número sobre el que se desea conocer su tabla de multiplicar y escribir esta a continuación por pantalla.

Proceso TablaMultiplicar
Definir a,b,c Como Entero;
Escribir "Tabla de multiplicar del número...";
Leer b;
Escribir Concatenar("TABLA DE MULTIPLICAR DEL NÚMERO ", ConvertirATexto(b));
Para a<-0 Hasta 10 Con Paso 1 Hacer
c<- b*a;
Escribir ConvertirATexto(b) + " * " + ConvertirATexto(a) + " = " + ConvertirATexto(c);
Fin Para
FinProceso


Como puedes comprobar...
  • En primer lugar definimos tres variables y las declaramos de tipo entero.
    • La primera (a) es la variable que utilizaremos como variable de la estructura Para, la que define su valora inicial y final, lo que equivale a decir que determina el número de ciclos que recorre el bucle. Cumple además la función de multiplicador.
    • La segunda (b) es la que se pide ingresar al usuario/a y cumple la función de multiplicando.
    • La tercera (c) será empleada como producto de la multiplicación.
  • Posteriormente solicitamos al usuario/a que introduzca un número. Ese número debe ser entero, ya que así se declararon las variables, incluyendo la variable b, que es a la que se asigna el valor introducido por teclado.
  • En tercer lugar articulamos el bucle Para utilizando la variable (a) como referente para la asignación del valor inicial y final (podemos plantear los que deseemos) y especificamos el paso o incremento que deseemos se realice cada vez que funciona el bucle, aunque, como dije antes, no es necesario, ya que el paso 1 es el que se ejecuta por defecto.
  • Finalmente creamos la sentencia que deseamos se repita. En este caso se trata de la asignación del resultado de la operación b*a a la variable c. Obsérvese que aprovechamos la modificación del valor de a que obtenemos del recorrido del bucle Para para utilizarla como multiplicador.
El flujograma de este algoritmo es el siguiente:

El óvalo situado por encima de este párrafo representa las condiciones que afectan a la variable a: valor inicial, paso y valor final, y las líneas que le unen (y acotan) al inicio del ciclo y a su final, representan la iteración misma.

También se puede representar el bucle para del siguiente modo:

Fuente: Figura 1. Team 5. Estructuras iterativas

Se trata en este caso de un bucle Para teórico y genérico incremental positivo, similar al que empleamos en nuestro algoritmo. Aunque sea redundante con el anterior PSeInt, me ha parecido conveniente exponerlo, ya que es la forma en que generalmente se puede ver representado un ciclo Para (PSeInt sintetiza aquí en exceso): lo que explica este flujograma, creo que mejor que la representación que hace PSeInt, es que:
  1. Se valora si se cumple la proposición Variable > Valor.
  2. Si esta valoración da como resultado F, se ejecuta la secuencia indicada en el eje central.
  3. Al finalizar esta ejecución Variable se incrementa en Paso (Variable=Variable+Paso) y se regresa al inicio del bucle (eje derecho del flujograma).
  4. Se repite 1. Si el resultado es F de nuevo, se repiten 2-3. Pero si el resultado es V se ejecuta el eje izquierdo, que equivale a salir del bucle y continuar con la ejecución del algoritmo (en nuestro caso, finaliza el algoritmo).
Con esto damos por finalizada esta entrada. Continuaremos en las próximas viendo cómo se construye el bucle para en OOo Basic y en Python.

jueves, 15 de septiembre de 2022

Introducción. Lenguajes.

Iteración. Concepto

Del mismo modo que las estructuras condicionales vistas en las entradas anteriores nos permiten establecer itinerarios o bifurcaciones diferentes en un algoritmo en función del cumplimiento o no de una o varias condiciones, las estructuras denominadas normalmente bucles o ciclos, nos permiten establecer iteraciones de forma que se facilitan la estructuración del algoritmo, simplificando su elaboración y ejecución. Pero ¿qué es un bucle?, ¿qué significa iterar?. Vamos a tratar de estos conceptos en esta entra y en las siguientes.

La RAE define Iterar (del latín iterare) como sinónimo de repetir, derivando del verbo el sustantivo iteración (repetición) y el adjetivo iterativo/a

Como sinónimos de tenemos los términos verbos repetir o el reiterar, entendidos ambos como volver a desarrollar (realizar) una acción o pronunciar de nuevo lo que ya se había dicho.

Pero es respecto al adjetivo derivado iterativo/va donde la RAE aporta mas significados:

  • Que se repite o que indica repetición (reiterativo acentúa el carácter repetitivo de lo subyacente)
  • Y un significado específico en el ámbito de las matemáticas, entendido como procedimiento o método que llega al resultado mediante aproximaciones sucesivas, partiendo de una estimación inicial. Estos métodos matemáticos resultan útiles para resolver problemas con infinidad de variables

Existe otro concepto matemáticos relacionado con la iteración: la función iterada, que es aquélla que se compone de sí misma. Como función compuesta se logra a partir de la aplicación sucesiva de otras funciones, lo que implica que la iteración de una función constituye la creación de una función compuesta a partir de la repetición de la propia función.

Aunque este campo conceptual se muestra muy prometedor, especialmente  como conceptualización, aunque también como metodología de trabajo para abordar la resolución de problemas complejos, se encuentra relativamente alejado del uso que puede resultar de interés para nuestros objetivos concretos, y mucho más de mis capacidades de comprensión desde una perspectiva de funcionalidad. 

No obstante, intuyo la posibilidad de que un análisis más informado de ciertas formas de expresar la asignación de variables esté más próxima de lo que puede parecer al concepto de iteración de una función. Además, el  uso de la iteración para la creación y manipulación de sucesiones es mucho más funcional de lo que esta aproximación conceptual abstracta permite intuir, incluyendo su aplicación al trabajo con colecciones de datos. Aun así, y en todo caso, aun son meras intuiciones, por lo que es largo el camino que tendré que recorrer para poder expresarme con mayor propiedad y seguridad. En todo caso, estos conceptos parece ayudar más a la comprensión de productos y al tratamiento formal de los contenidos (las variables), que a las formas, formulaciones o estructuras que los hacen posible, cuestiones estas fundamentales desde la perspectiva de quien se inicia en la programación y en su lógica.

Por ello resulta de mayor utilidad inmediata y práctica para nuestros objetivos actuales la información que aportan los textos específicos sobre programación, ya que la RAE no contempla este campo específico (no al menos en la fuente consultada a la fecha 15/09/2022).

Esta fuente define las estructuras o sentencias iterativas como aquellas que permiten varias veces algunos segmentos del programa. Como se puede deducir, y de acuerdo con lo dije en la entrada anterior, iniciamos con ellas es aprendizaje de recursos de los lenguajes de programación orientados a facilitar el desarrollo del algoritmo y su funcionamiento, a la vez que a simplificar su formulación. 

No obstante, desde el punto de vista de la lógica de programación, estas estructuras iterativas, también denominadas más comúnmente bucles o ciclos, presentan cierta complejidad en su formulación y uso, por lo que será conveniente comprender correctamente su lógica de funcionamiento para que el uso que hagamos de ellas sea correcto y realmente nos faciliten el trabajo.

Distinguimos con los anteriores autores dos tipos básicos de bucles:

  • Los denominados deterministas, que son aquellos en los que conocemos a priori el número de repeticiones.
  • Y los indeterministas, que yo me atrevo a llamar también condicionados, que son aquellos en los que desconocemos el número de repeticiones y éstas están condicionadas al cumplimiento de una determinada condición.
Puesto que el tema es complejo y es necesario subdividirlo para abordarlo con mayor garantía de éxito, me planteo dar por finalizado esta entrada, anunciando continuidad temática para la próxima, en la que trabajaremos sobre los bucles deterministas.

Dejo a tu disposición, eso sí, algo de literatura respecto este tema. Salvo el documento de la Universidad de Málaga (ya citado, pero que reitero), el resto son enlaces a páginas informativas, básicas a nivel conceptual y clarificadoras. Al menos para mí lo han sido, cosa que agradezco a los autores y autoras.
  • Iteración. Significado.
  • Definiciones. Iteración.
  • Definiciones. Bucle.
  • Apuntes de Informática. Departamento de Lenguajes y Ciencias de la Computación. Universidad de Málaga.