Mostrando entradas con la etiqueta Bucle. Mostrar todas las entradas
Mostrando entradas con la etiqueta Bucle. Mostrar todas las entradas

domingo, 5 de octubre de 2025

Lenguajes. Python.


Listas y tuplas



Lista como iterable

Ya hemos tenido ocasión de comprobar la utilidad que tiene el uso de un array como iterable en la automatización de procedimientos cuando trabajamos con OOo Basic. Uno de esos iterables en Python son las listas, de ahí el interés por saber iterar por ellas.

De hecho la iteración se presenta en Python más sencilla y a la vez más variada en cuanto procedimiento; y también más potente. Trataremos en esta entrada sobre las diferentes formar de iterar por una lista... y algo más.

Partiendo de la estructura for como instrucción básica para la iteración, la forma más sencilla de iterar es tomar como referente el elemento y como iterable la lista. Así, dada una lista (vg. los curso de E. Primaria)...

cursos = ['P1','P2','P3','P4','P5','P6']

... podemos recorrer la lista del siguiente modo:

for curso in cursos:

    print(curso)

Otra forma de iterar es  obtener el número de elementos de la lista mediante la función len() y usar la variable asociada (o la misma expresión) como límite superior del recorrido a realizar (1):

n = len(cursos)

for i in range (n):

    print(cursos[i])

En ocasiones nos puede venir bien recuperar a la vez el dato y el índice que, como elemento, ocupa en la lista. Para estos casos disponemos de un método específico:

for index, curso in enumerate (cursos):

    print(index, curso)

Otra situación especial, que no podemos contemplar como específica en OOo Basic, aunque podemos simularla de otro modo, es cuando disponemos de dos  o más listas y deseamos iterar por ambas, simultaneándolas:

cursos = ['P1','P2','P3','P4','P5','P6']
intervalos = ['2000/2001','2001/2002','2002/2003','2003/2004','2005/2006','2006/2007']

for curso, intervalo in zip(cursos, intervalos):

    print(intervalo, curso)

Finalmente también podemos iterar secuencialmente (2) por dos o más listas. Para ello primero importamos el módulo itertools y después usamos el método itertools.chain(), como vemos en este ejemplo:

import itertools
for item in itertools.chain(cursos, intervalos):
    print(item)

A partir de aquí ya podemos utilizar las listas para automatizar procedimiento al mismo nivel que hacemos en OOo Basic, sólo de con mayor facilidad y potencia. 


NOTAS 

(1) Obsérvese el modo diferente en que construimos la expresión de escritura en los dos métodos anteriores: en el primero es el referente (contador, si se prefiere, aunque en este caso no es esa su función principal) el que usamos como contenedor del dato (print(curso)), pero en el segundo identificamos directamente el elemento de la lista mediante su índice, siendo éste el valor actual del contador (print(cursos[i])). Como recordarás, este segundo método se asemeja más al modo en que trabajamos en OOo Basic.
(2) Secuencialmente se opone aquí a simultáneamente, lo que implica que zip() se opone a itertools.chain(). zip() es una de las funciones built-in que puedes consultar en [esta entrada]

martes, 3 de junio de 2025

Textos. OOo Basic

Matrices como recurso para generar datos (output)


También podemos utilizar matrices asociadas a bucles como recurso para generar datos de salida. En realidad no es un procedimiento muy diferente al que empleamos para [facilitar el input] como podremos ver en esta entrada.



En este caso tenemos que dar por supuesto que la matriz de datos cuenta con todos los necesarios, sin entrar en detalles sobre el modo en que llegamos a esta situación (1). Suponemos también que nos enfrentamos a la tarea de automatizar la generación del texto de salida (output) (2) siendo el script el que sigue:

Dim mDatos(7) As String, vTexto As String
Dim i As Integer

mDatos(0) = "Con fecha"
mDatos(1) = "12 de mayo de 2024"
mDatos(2) = "en el CP Caudalia"
mDatos(3) = "tiene lugar la reunión de tutoría"
mDatos(4) = "con la madre"
mDatos(5) = "del alumno"
mDatos(6) = "Carlos López"

Para reconfigurar los segmentos textuales contenidos en la matriz en un único string (3), usamos el bucle For como sigue

vTexto = ""

For i = 0 To UBound(mDatos())
vTexto = vTexto & " " & mDatos(i)
Next

MsgBox (vTexto)

Quede aclarado que MsgBox() suple aquí el procedimiento de escritura del texto resultante en el documento (output), simplificación que me permito por motivos didácticos. Pero lo importante es explicar el funcionamiento del bucle.
  • Recorremos la matriz mDatos() desde su inicio (i = 0) hasta su final (UBound(mDatos()) mediante el bucle For (For i = 0 To UBound(mDatos()))...
  • Asignando a la variable vTexto el contenido de cada uno de los elementos de la matriz (vTexto = vTexto & " " & mDatos(i))...
  • Instrucción en la que quiero incidir en dos detalles:
    • La recursividad: en el nuevo ciclo, sobre la variable vTexto escribimos el contenido de la propia variable en el ciclo precedente (vTexto = vTexto & ...)
    • El añadido de una cadena vacía como separador entre segmentos (& " " &), que no sería necesaria si cada segmento ya la incluyera como elemento final de su string, solución que no recomiendo.
El resultado presenta una pequeña "deficiencia": el string resultante se inicia con un espacio en blanco debido al modo en que se construye la secuencia. Para la mayoría de los usos del procedimiento esto puede resultar irrelevante, así que la solución anterior puede ser más que suficiente. No obstante si nos interesa eliminar ese espacio al inicio de la cadena podremos utilizar la alternativa que sigue...

For i = 0 To UBound(mDatos())
If i = 0 Then
vTexto = mDatos(i)
Else
vTexto = vTexto & " " & mDatos(i)
End If
Next

... en la que mediante un condicional diferenciamos el procedimiento al inicio del ciclo (If i = 0 Then -> vTexto = mDatos(i)) en el que asignamos directamente a vTexto el contenido del primer elemento de la matriz (i = 0) sin el ahora innecesario espacio en blanco, de las siguientes fases del mismo, en las que mantenemos el procedimiento ya explicado (Else -> vTexto = vTexto & " " & mDatos(i)).

NOTAS

(1) Aunque es fácil plantearlo de este modo, en realidad se trata de una cuestión que está cargada de interrogantes, pero ahora simplemente no toca plantearlos.
(2) Supongamos que es parte de la automatización de la composición de un documento
(3) Que reconstruimos sobre la variable vTexto, por lo que inicialmente asignamos a ésta el contenido cadena vacía (vTexto = "") como forma de clarificar el funcionamiento esperado.

Textos. OOo Basic

Matrices como recurso para la entrada de datos

Con ser muy importante la mejora  que permite el [cambio de matrices por variables] para el manejo del código, no es la única ventaja que aporta, ya que esta sustitución facilita el acceso a las posibilidades que supone el uso de estructuras de iteración. En esta entrada concretaremos esa ventaja en la dase input o de solicitud de datos al usuario.



Ya hemos explicado que el uso de InputBox() es probablemente la forma más simple de resolver el problema de cómo aportar al script los datos que se necesitan para cumplir su objetivo, sea éste el que sea (1)...

vAlNom = InputBox("Nombre del alumno o alumna: ")

... pero cuando tenemos que repetir esto 20 o treinta veces esta sencillez se convierte en molesta. Aquí caben dos alternativas: crear una función o emplear un bucle.

En realidad, lo de la función no deja de ser simplificar el código, pero no evita tener que repetir la llamada a la función tantas veces como variables tengamos... o cuantos elementos tenga la matriz. Sin embargo, la segunda alternativa, el uso de un bucle, sí resuelve el tema, tanto si (además) utilizamos una función como si no. Claro que para ello es necesario utilizar matrices en lugar de variables (2).

Veamos un ejemplo:

  • Tenemos una matriz con 10 elementos -> Dim mDatos(10) As String
  • Declaramos además una variable contador -> Dim i As Integer
  • Mediante la estructura For recorremos la matriz aplicando la función InputBox() a cada elemento:
For i = 0 To UBound(mDatos())

mDatos(i) = InputBox()

Next

De este modo, y de forma sencilla, el usuario introduce los datos en cada uno de los diez elementos de la matriz sin que sea necesario repetir diez veces el mismo proceso. No obstante, tenemos un problema para el que existen dos soluciones: el usuario no sabe qué información se le solicita, así que no sabe qué introducir, ya que le falta información aclaratorio en el InputBox(), la cual sí estaba disponible en el formato anterior (InputBox("Nombre del alumno o alumna: ")).

Este problema no se resuelve en el ciclo del mismo modo que fuera de él, ya que no se solicita 10 veces (por ejemplo) el mismo dato. Debemos buscar otra solución, porque no podemos esperar que se resuelva solo recurriendo a que el usuario es conocedor del documento, ni incluso que sea la misma persona que hizo el script. 

La solución más sencilla es crear, aportar (y tener a mano en el momento adecuado) un documento informativo que asocie cada elemento de la matriz con su contenido.

Ítem 0 -> Nombre del alumno 
Ítem 2 -> Apellidos del alumno 

... En este caso sería necesaria una pequeña modificación en el InputBox() (InputBox("Ítem " & i)o similar).

La segunda opción es más costosa pero más apropiada, además de posiblemente también más funcional (3): crear una segunda matriz tipo string cuyos elementos sean sencillas cadenas de texto que informan sobre el contenido de cada elemento de la matriz de datos (4)...

Dim mConten() As String
mConten = Array("Alumno. Nombre","Alumno. Apellidos","Alumno. Fecha de nacimiento"...)

... siendo necesario modificar también el InputBox() (InputBox(mConten(i))

NOTAS

(1) Por ejemplo, la automatización de la escritura de un texto. La forma más sencilla de disponer de un dato tan simple como necesario como es el nombre del alumno es preguntarle al usuario que trabaja sobre el documento que hemos automatizado.
(2) En realidad podemos utilizar variables (y no matrices), pero necesitamos que el nombre de las matrices se asemeje al formato de los elementos de la matriz, así que no deja de ser un sucedáneo del uso de matrices.
(3) Evitamos el riesgo de que se pierda el documento que contiene el listado anterior.
(4) Aun en este caso el código es más limpio y sencillo que utilizando reiteradamente la forma primitiva de la instrucción de input

domingo, 27 de octubre de 2024

Python. Lenguaje.

Iteración. Bucles.

Literación no es otra cosa que la ejecución de una instrucción de forma repetida. Junto con  la sucesión lineal y la bifurcación condicional, la iteración es una de las formas que pueden presentar el desarrollo de un algoritmo. Los bucles son las instrucciones (o estructuras instruccionales) que concretan cómo hacerlo y la potencia de procesamiento de los ordenadores la que permite que se realice de forma rápida y eficiente.


Mediante los bucles podemos recorrer amplios conjunto de datos para encontrar los que deseemos, también podemos crear respuestas sucesivas en función de determinadas condiciones. En cualquier caso estamos hablando de dos cuestiones que se alejan del carácter básico que tiene como objetivo esta entrada (1), en la que me limitaré a exponer las estructuras de iteración.

El lenguaje Python cuenta con dos estructuras de iteración o bucles: el bucle for y el bucle while, que permiten dos formas diferentes de iteración (2): una (for, "para") mediante un iterador y la otra (while, "mientras") en función del cumplimiento de una condición. En esta entrada mostraré la forma básica de uso de ambos, dejando para posteriores una explicación más detallada de las mismas.

El bucle for cuenta con una variable, un objeto iterable y un bloque de código. Un iterable no es otra cosa que un conjunto de valores que va tomando la variable en cada paso de la ejecución del bucle. Son muchos los objetos iterables en Python, además de los que podemos construir nosotros (3).

Una forma básica de crear un iterable en Python es la siguiente...

for n in (1,2,3,4,5):

    print("El valor de n es",n) 

La variable n adopta sucesivamente los valores indicados en el iterable (1,2,3,4,5) al que se referencia (n in (1,2,3,4,5)). Todo ello se muestra (visualiza) gracias a la instrucción o bloque de código subordinado que sigue ( print("El valor de n es",n) ) y que es lo siguiente:

El valor de n es 1
El valor de n es 2
El valor de n es 3
El valor de n es 4
El valor de n es 5

Otra forma de expresar un bucle for es la siguiente (4):

num = 5

for n  in range(1,11):

    print(n,"x",num,'=',n*num) 

... que nos devuelve:

1 x 5 = 5
2 x 5 = 10
3 x 5 = 15
4 x 5 = 20
5 x 5 = 25
6 x 5 = 30
7 x 5 = 35
8 x 5 = 40
9 x 5 = 45
10 x 5 = 50

La función range() cuenta con tres parámetros o valores: el inicial, el final y el de incremento (opcional). Mediante range(1,11) obtenemos un objeto iterable formado por los valores 1, 2, 3, 4, 5, 6, 7, 8, 9 y 10, que son los que necesitamos para crear la tabla de num (en nuestro caso, del 5). Observa que si hiciéramos algo parecido mediante la primera formulación no obtendríamos ese resultado, ya que no se utiliza el valor num, únicamente se itera por los posibles valores de (5). La función range() simplifica la expresión del iterable, haciendo innecesaria la enumeración de sus contenidos concretos y permitiendo establecer series de mayor complejidad (mediante el valor de incremento)

Una tercera forma de iterar es partiendo de un objeto iterable previamente definido y utilizarlo en el bucle como tal (como iterable) que recorremos mediante una variable. Un ejemplo (6)...

dias = ['lunes','martes','miércoles','jueves']

for dia in dias:
    print(dia)

... que nos devuelve

lunes

martes

miércoles

jueves

El bucle while tiene otra lógica de funcionamiento, ya que se basa en el cumplimiento de una condición que previamente se debe establecer (7) y que es previsible que se modifique a lo largo del recorrido del bucle (8). Además de las instrucciones que se desarrollen en su interior, while puede contener una instrucción o cláusula else, cuya función es establecer las instrucciones que se cumplirán cuando la condición deje de ser V. Esta cláusula es opcional.

Veamos un ejemplo de bucle while:

num = 0

while num < 10:
    print (num)
    num = num + 1 
else:
    print ("Has llegado a la meta") 

En este script establecemos un valor de inicio para la variable num (num = 0), lo que permite que el el bucle se inicie sin generar error. A continuación establecemos la condición del bucle (while num < 10), esto es: este bucle se ejecutará mientras la condición num < 10 sea True. Mientras esa condición se cumpla, el bucle nos devolverá como salida la escritura del valor que toma num (esto es, se cumple la instrucción que contiene:  print (num)).

Para evitar un bucle infinito, en este caso hemos optado por incorporar una instrucción de tipo contador (num = num + 1) que modifica el contenido de la variable, con lo que, en un momento determinado, la condición de partida dejará de ser True para pasar a False. En ese momento entra en funcionamiento la cláusula else, por lo que la salida final de bucle será el contenido de la instrucción asociada a else (print ("Has llegado a la meta")).

Otra forma muy común de trabajar con el bucle while consiste en establecer una variable de control del bucle con valor inicial True, que será la condición de ejecución del propio bucle. Dentro del bucle realizaremos alguna comprobación con un condicional que servirá para modificar el valor de la variable de control y finalizar la ejecución del bucle, al dejar de cumplirse la condición en que se basa.

print('Escribe un número par') # Un número impar servirá para terminar el bucle
seguir=True
while seguir:
    numero=input('Número: ')
    numero=eval(numero)
    if numero%2==1:
        seguir=False
else:
    print('Ha finalizado el bucle.', numero, 'es impar')

En este caso, el cambio de la condición (que quedó definida expresa y previamente en la variable booleana seguir) es el resultado del cumplimiento de una condición establecida dentro del bucle mediante una estructura condicional (if numero%2==1) coherente con el contenido del bucle (las instrucciones que desarrolla ( numero=input('Número: ') -> numero=eval(numero)). Nótese que la instrucción else pertenece al bucle while, no al condicional if (9)

Son muchas las cosas que tenemos que aprender sobre los bucles y sus usos, pero de momento disponemos ya del conocimiento suficiente para integrarlos en sencillos script de aprendizaje, alguno de ellos, incluso, con cierta funcionalidad práctica; aunque esta no sea, por el momento, nuestro objetivo.
NOTAS

(1) Lo expresado remite a las colecciones de datos (matrices) y al uso combinado de bucles y condicionales, temas que se abordarán más adelante.
(2) Tema este también de suficiente complejidad como para requerir un abordaje específico que no corresponde realizar ahora.
(3) Me refiero a las listas, las cadenas de texto, las tuplas... Sobre todas ellas, como iterables, hablaremos en su momento, aunque, por motivos didácticos, utilizaremos ahora algunas de ellas en la ejemplificación del funcionamiento de la iteración. 
(4) Código básico de la tabla de multiplicar del 5.
(5) En el segundo ejemplo, esa iteración de n por el range() queda expresada en el primer valor de lo que devuelve print(). Para replicar el mismo objetivo deberíamos crear el siguiente script:
num = 4
for n in (1,2,3,4,5,6,7,8,9,10):
    print (n,'x',num,'=',n*num)
(6) Que supera los objetivos de esta entrada y que explicaremos en otro momento.
(7) Esta condición determina el desarrollo del bucle, ya que éste sucede mientras se mantenga la condición original, esto es, hasta que esta deje de ser True (V).
(8) En caso contrario daría lugar a un bucle infinito, situación que, obviamente, deberemos evitar.
(9) En función del nivel de sangrado o indentación, que la ubica a la altura de while, no de if. En caso de encontrarse a la altura de if pertenecería a ese condicional y no al bucle. Véase la importancia de la indentación en Python y de lo necesario que es ejecutarla correctamente.

Python. Lenguaje.

Condicionales en Python


Lbifurcación es un componente fundamental de cualquier programa, por simple que éste sea. Obedece a la opcionalidad que resulta de los propios datos y del desarrollo lógico del algoritmo. Algo tan simple como la correcta identificación del género o número gramaticales es, para un algoritmo, una cuestión de bifurcación. La bifurcación se resuelve, en términos de lenguaje de programación, con estructuras condicionales. 


El lenguaje Python cuenta con una única estructura condicional con tres niveles de complejidad. Esta estructura es análoga al Il-ElseIf-Else de OOo Basic, aunque con presenta características propias. Estas son:
  • En Python el condicional (al igual que el resto de las instrucciones) se escribe en minúsculas.
  • No existe sentencia de finalización, usando como alternativa del identificador del bloque condicional el indentado (sangrado) del código, que es obligatorio en Python, aunque  también recomendable en el resto de los lenguajes.
  • La instrucción ElseIf se simplifica como expresión 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 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 también 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.

Vamos a trasladar el código OOo Basic a código Python para mostrar similitudes y diferencias:

En OOo Basic:

If vEdad > 17 Then
     MsgBox "Eres mayor de edad"
End If 

En Python

      Print("Eres mayor de edad") 
Esta es la formulación más simple de un condicional y 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 nos muestra dos niveles: el primero, el de la sentencia if, y el segundo, el del bloque de código (en este caso una única sentencia print())
  • La segunda, la ausencia de Then y sentencias similares, y su sustitución por (:). Muy importante no olvidar estos dos puntos.
  • Y la tercera, la ausencia de sentencia de finalización (End If), sustituida por el fin del sangrado.
Si queremos controlar el caso en que no se cumpla esta condición, esto es, cuando la proposición vEdad >17 sea F, emplearemos la sentencia else.
    print("Eres mayor de edad")
Observa el modo en que se emplea else, qué posición ocupa respecto a if (a su misma altura), el uso de : al finalizar la sentencia y el mantenimiento del sangrado en la sentencia asociada.

Cuando queremos crear una estructura condicional anidada (un condicional dentro de otro condicional), debemos mantener la jerarquía que implica este anidamiento mediante el incremento del sangrado, como puedes observar en este tercer ejemplo:

 
Fíjate en 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 (se sangran) hacia la derecha..

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, dado que Python con cuenta con otra estructura condicional, sólo disponemos de la instrucción elif para construir condicionales múltiples que en otros lenguajes se pueden resolver mediante estructuras Select Case. Reproduzco a continuación el script sobre días de la semana creado en PSeInt (pseudocódigo) junto con su formulación en Python para que puedas comparar semejanzas y diferencias.

  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 operador de igualdad, ==), escribimos los términos de la comparación (variable [operador relacional] valor)
  • La línea elif también finaliza con dos puntos (:)
  • La instrucción o instrucciones que sigue a elif deben sangrase un nivel (un tabulador, cuatro espacios), igual que las que siguen a if o a else.
Ahora ya dispones del nivel de conocimientos suficiente para desarrollar algoritmos que impliquen el uso de la condicionalidad y den respuesta a las necesidades de bifurcación de la respuesta en función del las condiciones que se observen en los datos. Desde la opción de inicio de un test en función de la edad del niño, hasta la toma de decisiones sobre categorización NEAE en función de casuística y datos empíricos, pasando por la simple personalización de un texto en función del tratamiento gramatical masculino vs. femenino... lo que necesites. 

sábado, 26 de octubre de 2024

Lenguajes. Python.

 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.

miércoles, 22 de mayo de 2024

Lenguajes. Python.

Bucle no determinista (while)

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:

  • 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. (1)
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 WISC-AritméticaOtro 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 (2):

#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("*************************************************")

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.

NOTAS

(1) 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 carácter 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.
(2) He asignado a la variable vClaveValida un valor ficticio ("MiClave") que podrás sustituir por el que tu desees.