Programando desde 0: 08- Números aleatorios -- Repetición condicional con WHILE...DO.
-
- Senior Member
- Moderador
02/01/2012#1 Programando desde 0: 08- Números aleatorios -- Repetición condicional con WHILE...DO.LECCIÓN 8: Repetición condicional con WHILE...DO
Hemos visto la sentencia FOR, la cual sirve para repetir bloques de código un número específico de veces. Resultará muy útil para ciertas tareas, sin embargo, sabemos que no siempre servirá. En la lección anterior les di el ejemplo del PIN de un celular.
Como ejemplo más sencillo podríamos imaginar un programa que nos haga adivinar un número en diez intentos. Nosotros ingresamos un número y el programa nos dice si acertamos o no. En caso de acertar ganamos el juego y el programa termina, sino acertamos seguimos intentando hasta gastar nuestros diez intentos, en cuyo caso perdemos.
Un programa como este puede finalizar por dos condiciones:
**Acertamos el número.
**Gastamos el número de intentos sin lograr acertar.
Claramente un FOR no serviría para algo como esto porque iteraría 10 veces, acertemos o no. Entonces ¿cómo hacemos para decirle al programa que repita algo mientras se den ciertas condiciones y que se detenga si estas no se dan?
Aquí entra la sentencia WHILE de la que hablaremos en esta lección. Sin embargo, antes de abordar ese tema veremos como obtener números aleatorios en Pascal ya que eso resultará útil para muchas cosas.
-------------------------------------------------------------------------------------
Generar números aleatorios:
Para obtener un número al azar se utiliza la función random. Su sintaxis es la siguiente:
Random(x)
donde X es un entero (integer) que marca el número máximo que se puede generar. Random(x) devolverá un número aleatorio entre 0 y X-1. Por ejemplo, si escribimos random(50) obtendremos un número al azar entre 0 y 49 inclusive.
Si nosotros queremos obtener un número entre 1 y X, entonces tenemos que ser sutiles:nos mostrará en pantalla un número entre 0 y 9.Código:WriteLn(Random(10));
nos mostrará en pantalla un número entre 1 y 10.Código:WriteLn(Random(10) + 1);
Explicaré esto a continuación.
Veamos un ejemplo sencillo en el que el usuario ingresa cuál es el tope y el programa le devolverá un número aleatorio entre 1 y el número ingresado. Asumimos que el usuario ingresará un entero mayor que 1:Bien, este programa es bien sencillo de entender. Tenemos dos variables, x y valor_aleatorio. La primera será leída desde la entrada estándar y guardará el valor ingresado por el usuario. La segunda guardará el número al azar obtenido por la función random.Código:1 PROGRAM funcion_random; 2 3 Var 4 x, valor_aleatorio: integer; 5 6 BEGIN 7 randomize; 8 9 write('Ingrese un valor: '); 10 readln(x); 11 12 valor_aleatorio:= random(x) + 1; 14 writeln('El número obtenido entre 1 y ',x,' es: ',valor_aleatorio); 15 END.
Lo primero a notar es que en la línea 7 hacemos una llamada al procedimiento randomize. Este no lleva parámetro alguno, simplemente indica que luego en algún momento del programa será utilizada la función random.
Si no hacen esto no podrán usar esta función y tendrán un error en tiempo de compilación.
En la línea 10 obtenemos el valor de X. En la línea 12 asignamos a valor_aleatorio el valor obtenido por random(x) más 1. ¿Por qué más 1? Si random(x) genera un número aleatorio entre 0 y X-1 pero nosotros queremos uno entre 1 y X solo basta sumar 1 al resultado. Por ejemplo, si hiciéramos random(10) podríamos obtener los números 0, 1, 2, 3, 4, 5, 6, 7, 8 o 9. Si quisiéramos un número entre 1 y 10 basta sumar 1 al valor sorteado. Si sale el 0, al sumar 1 obtenemos el 1. Si sale 9, al sumar 1 obtenemos el 10, o sea, corremos todo el intervalo un lugar a la derecha.
-------------------------------------------------------------------------------------
Secuencia de repetición WHILE DO:
Comencemos entonces con WHILE. Esta instrucción nos permitirá definir las condiciones que harán que un bloque de código se repita. Por lo tanto estas repeticiones serán distintas en cada ejecución del programa, no como sucede con FOR.
WHILE...DO significa MIENTRAS HAZ.
Veamos antes que nada su sintaxis general:Esto significa, mientras se cumpla la condición ejecuta tu instrucción. Una vez hecha la instrucción, WHILE regresará a su encabezado y volverá a verificar la condición. Si esta sigue siendo verdadera volverá a ejecutarse. En caso contrario salteará la instrucción y el programa continuará su curso. No está de más decir que la condición puede ser compuesta y que la instrucción puede ser un bloque de instrucciones delimitado por BEGIN y END. De este modo la forma más genérica de WHILE sería asíCódigo:While condicion do Instrucción;
Veremos primero el ejemplo del que hablé hace poco, un programa que nos hará adivinar un número. Generaremos un número al azar entre 1 y 30 y tendremos 10 intentos para adivinarlo. Aquí usaremos RANDOM.Código:WHILE condicion DO BEGIN Intruccion1; Instruccion2; . . . InstruccionN; END;Este programa está totalmente comentado, por lo cual deberían entenderlo completamente. Les dejo aquí, la versión sin comentarios para que el código resulte más entendible:Código:PROGRAM AdivinadorSimple; CONST MAX_INTENTOS= 10; //El número máximo de intentos. TOPE= 30; //El número más alto que se puede generar. VAR numero_a_adivinar, numero_leido: INTEGER; adivinado: BOOLEAN; (*La variable adivinado será TRUE cuando el usuario acierte, y FALSE mientras esto no suceda.*) intento_actual: INTEGER; //Un contador que nos dirá en qué intento vamos. BEGIN randomize; //Decimos al programa que utilizaremos random en algún lado. numero_a_adivinar:= Random(TOPE) + 1; adivinado:= FALSE; //La inicializamos en FALSE porque aún no adivinamos nada. intento_actual:= 0; //Aún no hemos intentado adivinar nada. WriteLn('Ingrese números para intentar adivinar:'); WriteLn; WHILE (NOT adivinado) AND (intento_actual< MAX_INTENTOS) DO (*Vean este encabezado. El WHILE se repetirá mientras la variable adivinado sea FALSE porque tiene el operador NOT, y mientras a su vez el número de intentos sea menor que el máximo de intentos posibles. Si estas condiciones no se cumplen ambas a la vez, la condición AND vale FALSE y el WHILE termina.*) BEGIN intento_actual:= intento_actual + 1; //Sumamos 1 al número de intentos. Write(intento_actual,' --> '); //Mostramos el intento actual. ReadLn(numero_leido); //Leemos el número que el usuario ingresa. (*Si el número es correcto, la variable adivinado se vuelve TRUE, en caso contrario seguirá valiendo FALSE tal como al inicio.*) IF numero_leido= numero_a_adivinar THEN adivinado:= TRUE; END; (*El WHILE puede haber terminado por dos cosas, o adivinamos o perdimos. Si adivinamos entonces la variable adivinado será TRUE, en caso contrario será FALSE. Usamos eso para distinguir qué mensajes mostrar al usuario.*) IF adivinado THEN WriteLn('Felicitaciones ¡¡¡GANASTE!!!') ELSE WriteLn('Lamentablemente has perdido.'); ReadLn; //Simplemente para visualizar lo que el programa nos muestra. END.Como pueden ver uso dos constantes en este programa, MAX_INTENTOS y TOPE. Simplemente son para que el código sea más entendible y fácilmente modificable. Si yo quiero que ahora mi programa genere números entre 1 y 100 solo voy y modifico el valor de TOPE. Si quiero que el número máximo de intentos sea 20 y no 10, modifico el valor de MAX_INTENTOS y listo.Código:PROGRAM AdivinadorSimple; CONST MAX_INTENTOS= 10; TOPE= 30; VAR numero_a_adivinar, numero_leido: INTEGER; adivinado: BOOLEAN; intento_actual: INTEGER; BEGIN randomize; numero_a_adivinar:= Random(TOPE) + 1; adivinado:= FALSE; intento_actual:= 0; WriteLn('Ingrese números para intentar adivinar:'); WriteLn; WHILE (NOT adivinado) AND (intento_actual< MAX_INTENTOS) DO BEGIN intento_actual:= intento_actual + 1; Write(intento_actual,' --> '); ReadLn(numero_leido); IF numero_leido= numero_a_adivinar THEN adivinado:= TRUE; END; IF adivinado THEN WriteLn('Felicitaciones ¡¡¡GANASTE!!!') ELSE WriteLn('Lamentablemente has perdido.'); ReadLn; END.
En este ejemplo vemos por primera vez uno de los usos de las variables booblean. En este caso declaré una adivinado que indicará el momento en el cual el usuario adivine el número.
Comenzamos generándonos un número al azar y asignando ese valor a la variable numero_a_adivinar , para luego inicializar nuestro booleano con el valor false ya que el usuario no ha adivinado nada todavía porque ni siquiera ha intentado. También inicializamos la variable intento_actual con el valor 0 ya que el usuario aún no ha comenzado.
Es importante siempre inicializar las variables con los valores apropiados y no asumir que ya lo tienen. Es común que uno suponga que el valor de intento_actual sea 0 al inicio ya que no se le asignó nada hasta el momento, pero esto no es así, depende mucho del compilador que se use. Cuando uno declara una variable de algún tipo, esta puede tomar un valor cualquiera al azar que es desconocido por el usuario (basura). Hay compiladores que las inicializan con algún valor específico. Sin embargo, al crear un software, nunca sabemos en el futuro cuando deberemos modificarlo o si debemos pasarle nuestro código a algún otro programador para que él trabaje con él. Si la otra persona usa un compilador diferente entonces el programa probablemente no funcione como debe.
¡¡¡SIEMPRE INICIALIZAR LAS VARIABLES!!!
Veamos el encabezado del WHILE ya que allí las condiciones están dadas por el booleano y por una comparación.es equivalente a decirCódigo:WHILE (NOT adivinado) AND (intento_actual< MAX_INTENTOS) DO
Aquí vemos por primera vez el uso del operador NOT. Vean que lo que hace es preguntar por la falsedad de una variable booleana, o sea, la condición es verdadera mientras el valor de la variable sea FALSE. Si no se antepone el operador NOT y solo aparece el nombre de la variable booleana, estamos preguntando por la veracidad de la misma, o sea, si esta vale TRUE. Notar que la condición es una condición compuesta y que será verdadera en tanto ambas condiciones comprendidas entre el operador AND sean verdaderas, o sea, la condición es verdadera si el booleano es FALSO y la variable intento_actual es menor que MAX_INTENTOS. Si falla alguna de estas cosas el AND es FALSO y el WHILE terminará su iteración.Código:WHILE (adivino=FALSE) AND (intento_actual< MAX_INTENTOS) DO
Usar el nombre de un booleano en una condición ya es preguntar por si es TRUE. Del mismo modo si uno quiere preguntar por si es FALSE, como en nuestro programa, debe anteponer el operador NOT antes del nombre del booleano. Por ejemplo:Es lo mismo que decirCódigo:IF adivinado THEN Instruccion;
Ya saben que si ponen NOT es justamente lo contrario.Código:IF adivinado=TRUE THEN Instruccion;
Bien, nuestro WHILE funcionará en tanto se cumplan las condiciones dadas, basta que una falle para que no se ejecute el WHILE. Como nosotros inicializamos nuestro booleano en FALSE y le dimos a intento_actual el valor 0, sabemos que la primera vez que el programa llegue al WHILE su condición AND será verdadera y por tanto se ejecutará al menos una vez. Esto es porque nosotros hemos dado a propósito las condiciones necesarias.
Lo primero que hacemos dentro del bloque del WHILE es la asignación:o sea, estamos asignando a una variable su valor anterior más 1. Esto se conoce como acumulador porque es una variable que acumula valores y va aumentando. ¿Se dan cuenta de eso? En la primera ejecución del WHILE intento_actual pasa a valer 1.Código:intento_actual:= intento_actual + 1;
En cada iteración del WHILE estaremos aumentando esta variable en 1, por lo tanto nos aseguramos de que en algún momento alcanzará el número máximo de intentos disponibles.
Luego leemos el número que el usuario ingresa para pasar a esta instrucción:Lo que hace este IF es cambiar el valor de adivinado a TRUE si el usuario a adivinado el número. Si esto sucede, cuando el WHILE vuelva a mirar su encabezado se encontrará conque el booleano es TRUE y por lo tanto ya no iterará más y seguiremos de largo.Código:IF numero_leido= numero_a_adivinar THEN adivinado:= TRUE;
Esta asignación con IF, se puede hacer así:Como ven, es posible asignar a un booleano la condición que debe cumplirse para que sea TRUE, si esto no sucede se le asigna el valor FALSE. Esa sería la forma más correcta en vez de hacerlo con IF. Yo lo hice así primero para que a ustedes les quede bien claro lo que se está haciendo.Código:adivinado:= numero_leido= numero_a_adivinar;
El uso que dimos aquí de la variable booleana es conocido como Bandera Booleana ya que nuestra variable se encenderá cuando se cumpla alguna condición y nos avisará algo. Sería como levantar una bandera para indicarle algo a alguien. El uso de banderas booleanas es muy común aunque en principio uno no lo crea. A medida que la práctica en programar es mayor se comprende mejor a los booleanos y su uso aumenta. Yo en particular hago mucho uso de ellos, llegando incluso a abusarme un poco, cosa que no es aconsejable ya que el código puede volverse muy confuso.
Una vez fuera del WHILE debemos verificar por qué es que salimos de él. Eso hace el último IF, si salimos porque adivinado se volvió TRUE entonces será porque el usuario ganó, en caso contrario salimos porque el usuario se pasó de intentos posibles y perdió.
Muy bien, ahora que ya conocen el uso de WHILE es hora de darle práctica. Quiero que modifiquen el programa del triángulo para que cuando el usuario ingrese base o altura con un valor nulo o negativo el programa le notifique su error y vuelva a pedírselo. Asuman que el usuario siempre ingresa números reales en la entrada, no se preocupen todavía por si ingresa otros caracteres.
También modifiquen el programa que efectuaba una división entre dos números de modo que si el usuario ingresa el valor del cociente como 0 el programa se lo indique y vuelva a pedírselo.
Hagan lo mismo con el programa que desglosaba un número de cuatro cifras. Si el usuario ingresa un número inválido vuelvan a pedírselo hasta que sea válido.
===============================================
Un pequeño juego:
Los tres ejercicios anteriores corresponden a modificar códigos ya hechos por mí y a remodificar lo que ya habían hecho en los ejercicios anteriores, que aunque no es del todo fácil es más sencillo que diseñar un programa desde 0. Ahora les plantearé un pequeño juego para que lo hagan. Piensen en esto como en su primer proyecto de programación. Podrán usar todo lo que hemos visto hasta el momento. Este juego se centrará en el uso del WHILE. El problema es el siguiente:
Un jugador deberá adivinar en un máximo de 15 intentos un número entre 1 y 100. Si el jugador adivina, el programa se lo notificará y terminará su ejecución, en caso contrario el jugador recibirá una pista para volver a intentar adivinar. El juego le dirá si el número que debe adivinar es mayor o menor que el que ha ingresado y el jugador volverá intentarlo.
En cada caso el juego deberá mostrar al jugador el número de intento actual y cuantos restantes le quedan. Cuando el usuario pierda, el programa deberá mostrar cuál era el número que debía adivinar. Siempre asumiremos que recibiremos un entero desde la entrada estándar.
El número a ser adivinado será generado al azar por el juego.
Ejemplos de ejecución:
Ejemplo 1:
Dispones de 15 intentos para adivinar.
1)--> 98
¡¡¡Muy bien!!! ¡¡¡Has adivinado!!!
Ejemplo 2:
Dispones de 15 intentos para adivinar.
1)--> 99
Lo siento, no has acertado.
El número que debes adivinar es menor.
Dispones de 14 intentos para adivinar.
2)--> 80
Lo siento, no has acertado.
El número que debes adivinar es mayor.
Dispones de 13 intentos para adivinar.
3)--> 85
¡¡¡Muy bien!!! ¡¡¡Has adivinado!!!
Ejemplo 3:
Dispones de 15 intentos para adivinar.
1)--> 60
Lo siento, no has acertado.
El número que debes adivinar es menor.
. . .
Dispones de 1 intentos para adivinar.
15)--> 13
Lo siento, no has acertado.
Lamentablemente has perdido. El número era 10.
Mucha suerte. Espero que esto les guste. Un saludo, y como siempre, estaré a las órdenes.
Les dejo aquí un enlace para descargar este programa ya compilado como ejemplo para que vean como debe funcionar. Está en una versión para Windows, si alguien necesita una para Linux no dude en pedirla:
Programa Adivinador -
-
-
Member -
27/04/2012#5 Re: Programando desde 0: 08- Números aleatorios -- Repetición condicional con WHILE..ok muy bueno el reto
se me vinieron varias ideas a la mente y una pequeña duda
hay una forma con la cual yo pueda poner que me vaya sumando los intentos y escribiendome en la pantalla ejemplo
if intento_actual=false then
begin
writeln ('usted va por el intento' ,intentos_actual+1:2:2 ); {se que no ocurre nada pero buco que me muestre algo asi una forma que no tenga que poner tanta variable o writeln y que se vaya sumando cada intento actual cuando ingrese el num
end;
o si no lo hago simple ingrese en la variable 15 intentos actuales
o pongo simple writeln y q lo vaya mostrando en el intento que corresponda solo busco la manera de no compilarlo con el codigo tan largo
-----Agregado el 27/4/2012 a las 12 : 33 : 55-----
ok resuelta la duda solo
habia que pones
if intento_actual=false then
begin
writeln ('usted va por el intento' , intento actual+1)
end;
gracias asi se ve mejor que como lo estaba haciendo -
-
-
Member -
26/07/2012#7 Re: Programando desde 0: 08- Números aleatorios -- Repetición condicional con WHILE..Que hay Vlady_18, comenzaron las preguntas de mi parte, bueno. Me voy a quedar en este apartado un rato porqué la verdad no consigo asimilar el WHILE puff...si entendí lo que explicaste y demás, pero al ponerlo en práctica me bloqueo, no se dónde poner el WHILE, si declarar más variables o no, jeje ya te empezaré a dar lata...algun otro consejo aparte de los que nos das en esta lección...de antemano te agradezco todo. Que estes muy bien.
Te menciono también que el programita que hice se repite y se repite incluso tengo que cerrar el freepascal...Última edición por danteolivetti; 26/07/2012 a las 03:35
-
-
-
Senior Member - Moderador
26/07/2012#8 Re: Programando desde 0: 08- Números aleatorios -- Repetición condicional con WHILE..Amigo, la idea es justamente que pregunten. Es normal que al leer la teoría uno diga "Ahhh, esto es super sencillo, lo hago en dos segundos", y luego al sentarse a escribir código salen preguntas como "¿Donde pongo esto?", "¿Cómo se si eso va ahí?","¿Por qué no funciona si lo he hecho igual que el ejemplo?", etc etc...
Te pediré que pegues tu código aquí así te ayudo con él y de paso te doy consejos sobre tus errores.
Saludos. -
-
-
Member -
27/07/2012#9 Re: Programando desde 0: 08- Números aleatorios -- Repetición condicional con WHILE..Este es el código:
________________________________________________
PROGRAM AreaTriangulowhile;
VAR
base, altura: integer;
area: integer;
BEGIN
write('Ingresa la base: ');
readln(base);
write('Ingresa la altura: ');
readln(altura);
area:= base*altura/2;
WHILE (area<=0) DO
begin
write('Valor no valido, ingresa un número mayor a cero: ')
if area>0 then
writeln('El area del triangulo es: ',area);
end;
readln;
END. -
-
-
Senior Member - Moderador
27/07/2012#10 Re: Programando desde 0: 08- Números aleatorios -- Repetición condicional con WHILE..Veamos amigo, tu WHILE tiene como condición esto:
area<=0
Esto significa que mientras eso se cumpla el WHILE seguirá iterando, es decir, al llegar al final de su bloque volverá a su encabezado, verificará si el valor de área es menor o igual que 0 y de ser así volverá a entrar en su bloque, tras lo cual esto se repite hasta que en algún la condición no se cumpla.
Antes del WHILE tu lees los datos de base y altura, luego calculas el área. Ahora, veamos el bloque de tu WHILE:
Supongamos que el usuario ingresó como base 0 y como altura 10 por tanto el área dará 0, de este modo entraremos en tu WHILE ¿me sigues?
Lo primero es la instrucción que muestra un mensaje al usuario y pide que se ingrese un número mayor a cero. ¿Donde lees dicho valor? O sea ¿dónde luego de ese mensaje el usuario ingresa el valor? Pues en ningún lado porque no has puesto ninguna instrucción de lectura.
Bien, luego tenemos el IF que verifica que área sea mayor que 0, pero en ningún lado la haz vuelto a calcular, es decir, valía 0 antes de entrar al WHILE y sigue valiendo 0 ahora porque no haz hecho nada que modifique dicho valor, por tanto el IF no se ejecutará nunca.
Llegamos al end de tu WHILE por tanto volveremos a su encabezado. El WHILE verificará si área es menor o igual que 0. Pues sigue siendo 0 porque nunca cambió, entonces vuelve a entrar, vuelve a mostrar el mensaje al usuario y nuevamente no hay nada que modifique el valor de la variable area. Esto se repetirá infinitamente, es decir, tu programa quedará sumergido en un bucle infinito.
¿Cómo arreglamos esto? Pues luego de mostrar el mensaje al usuario deberías volver a pedir que se ingrese la base y la altura. Con esos nuevos valores debes volver a calcular el área.
La instrucción que muestra el mensaje al usuario colócala fuera del WHILE y sin el IF. ¿Por qué? Pues porque si a la primera el usuario se equivoca ingresando algún valor como 0 todo bien porque entramos en el WHILE y en algún momento se mostrará el mensaje del área, pero... ¿que pasa si de primera el usuario ingresa todo bien? No entraremos en el WHILE y por tanto no se mostrará ningún mensaje al usuario con el área calculada.
¿Por qué debo sacar la instrucción del WHILE y por qué debo dejarla sin el IF que verifica que área sea mayor que 0? Pues porque colocas una instrucción fuera del WHILE para mostrar el área y dejas la que está ahí tu programa funcionará bien solo en caso de que de una el usuario ingrese valores correctos. Si se equivoca, pues entraremos en el WHILE, se repetirá tanto como haga falta y en algún momento el WHILE mostrará el mensaje al usuario con el área calculada. Luego al salir pues se mostrará de nuevo el área calculada. Entonces, solo debe haber una instrucción que muestre el área al usuario y esta debe ir fuera del WHILE sin el IF. Esto último es porque si el usuario se equivoca entraremos en el WHILE hasta obtener un área válida y por tanto luego la mostraremos. Si el usuario no se equivoca, no entraremos en el WHILE y por tanto luego mostraremos el mensaje al usuario. En pseudocódigo tendríamos
*Leemos los valores de perimetro y altura.
*Calculamos el área.
*Mientras area=0
Leemos los valores de perimetro y altura
Calculamos el area
*Mostramos el area final al usuario. -
