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.

No hay comentarios:

Publicar un comentario

Comenta esta entrada