Ciclos - FisMat

Anuncio
Ciclos
H. Tejeda
Marzo 2016
Índice
1. Estructura de ciclo
1
2. Creación de ciclos while
2
3. Operadores aritméticos abreviados
7
4. Creación de ciclos for
9
5. Creación de ciclos do...while
11
6. Ciclos anidados
13
7. Rendimiento de ciclos
15
1.
Estructura de ciclo
Con las decisiones se hace que un programa parezca inteligente, con ciclos se hace que el programa
se vea poderoso. Un ciclo, o bucle, es una estructura que permite repetir la ejecución de un bloque
de sentencias. Una expresión booleana es evaluada dentro de la estructura de ciclo. Si la expresión es
true, un bloque de sentencias, cuerpo del ciclo, se ejecuta y la expresión booleana vuelve a ser
evaluada otra vez. El cuerpo del siglo puede ser una sola sentencia o un bloque de sentencias entre
llaves. Mientras la expresión booleana sea true, las sentencias en el cuerpo del ciclo se continúan
ejecutando. Cuando la evaluación booleana es false, el ciclo termina. Una ejecución de cualquier
ciclo se denomina iteración. En la figura 1 se muestra un diagrama de la lógica de un ciclo.
Se pueden emplear varios tipos de ciclos en Java. Se revisarán los siguientes tres tipos de ciclos.
Ciclo while, la expresión booleana que controla el ciclo es la primera sentencia en el ciclo.
1
Figura 1: Diagrama de flujo de una estructura de bucle.
Ciclo for, es una formato compacto para ejecutar ciclos.
Ciclo do...while, la expresión booleana que controla el ciclo es la última sentencia en el
ciclo.
2.
Creación de ciclos while
Se puede usar un ciclo while para ejecutar un cuerpo de sentencias mientras una expresión booleana que controla la entrada en el ciclo continúa siendo true. Un ciclo while consiste de la palabra
reservada while seguida de una expresión booleana dentro de paréntesis, seguida por el cuerpo del
ciclo.
Se usan un ciclo while cuando se necesita hacer una tarea un predeterminado número de veces o un
número impredecible de veces. Un ciclo que se ejecuta una cantidad especı́fica de veces es un ciclo
definido, o un ciclo contado. Por otra parte, la cantidad de veces que el ciclo se ejecuta podrı́a no
estar determinado cuando se está ejecutando el programa. Tal ciclo es un ciclo no definido ya
que no se sabe cuántas veces será ejecutado.
Ciclo while definido
Para escribir un ciclo definido se inicializa una variable de control del ciclo, cuyo valor determina
si la ejecución del ciclo continúa. Mientras el valor booleano que resulta de la comparación de la
variable de control de ciclo y otro valor sea true, el cuerpo del ciclo while se continúa ejecutando.
En el cuerpo del ciclo, se deberá incluir una sentencia que modifique la variable de control del ciclo.
El siguiente segmento de código muestra la secuencia de enteros del uno al diez. La variable val es
la variable de control del ciclo—esta inicia el ciclo teniendo el valor de uno, y mientras el valor es
menor que once, val continúa mostrándose e incrementándose.
int val;
final int LIMITE = 11;
val = 1;
while (val < LIMITE) {
2
System.out.println(val);
val = val + 1;
}
Cuando se escriben códigos conteniendo ciclos, es fácil cometer errores. La ejecución del siguiente
código muestra el mensaje “Hola” siempre (teóricamente) porque no hay código para terminar el
ciclo. Un ciclo que nunca termina se conoce como un ciclo infinito.
while ( 4>2 ) {
System.out.println("Hola");
}
En el código anterior, la expresión 4 > 2 evalúa a true ası́ que en ese ciclo, se entra al cuerpo del
ciclo y “Hola” es mostrado. Enseguida, la expresión es evaluada nuevamente. La expresión 4 > 2
todavı́a es true, ası́ que se ingresa al cuerpo otra vez. “Hola” se muestra repetidamente; el ciclo
nunca termina porque 4 > 2 nunca es false.
Es una mala idea escribir un ciclo infinito intencionalmente. Se sugiere que antes de empezar a
escribir ciclos infinitos, se sepa como salir de este ciclo. Si al ejecutar una aplicación está en un
ciclo infinito, se puede presionar las teclas CTRL-C para que el programa sea abortado.
Para prevenir que un ciclo while se ejecute infinitamente, tres acciones separadas deberán ocurrir:
Una variable de control del ciclo se deberá inicializar con un valor inicial.
La variable de control del ciclo es probada en la sentencia while.
La variable de control del ciclo es modificada dentro del cuerpo del ciclo. La variable deberá ser
modificada para que la expresión de prueba pueda eventualmente evaluar a false y ası́ el
ciclo pueda terminar.
Las condiciones anteriores se cumplen en el siguiente segmento de código. Primero, una variable de
control del ciclo contadorCiclo es puesta al valor uno. Segundo, la sentencia while(contadorCiclo
< 3) es probada. Tercero, el cuerpo del ciclo es ejecutado porque la variable de control del ciclo
contadorCiclo es menor que 3. El cuerpo del ciclo consiste de dos sentencias formando un bloque
con las llaves que las encierran. La primera sentencia muestra “Hola”, y luego la segunda sentencia
agrega uno a contadorCiclo. La siguiente vez que contadorCiclo es evaluado, este es dos, la
cual todavı́a es menor que 3, ası́ que el cuerpo del ciclo se ejecuta nuevamente. “Hola” se muestra por segunda vez, y contadorCiclo tiene tres. Finalmente, como la expresión contadorCiclo
< 3 evalúa a false, el ciclo termina. La ejecución del programa entonces continúa con cualquier
sentencia siguiente.
contadorCiclo = 1;
while (contadorCiclo < 3) {
System.out.println("Hola");
contadorCiclo = contadorCiclo + 1;
}
3
Errores al codificar ciclos while
Se muestran enseguida algunos errores de lógica que pueden aparecer al no tener cuidado al escribir
un ciclo while.
Variable de control del ciclo sin modificación en el cuerpo
La variable de control del ciclo deberá ser modificada dentro del cuerpo del ciclo. En el siguiente
código, parecido al anterior, se han eliminado las llaves. En este caso, el cuerpo del ciclo while termina en el punto y coma al final de la sentencia que muestra “Hola”. Agregar uno a contadorCiclo
no es parte de un bloque del ciclo, ası́ que el valor de contadorCiclo nunca cambia, y un ciclo
infinito es creado.
contadorCiclo = 1;
while (contadorCiclo < 3)
System.out.println("Hola");
contadorCiclo = contadorCiclo + 1;
Creación de un ciclo con un cuerpo vacı́o
Al igual que con la sentencia de decisión if, la colocación del punto y coma es importante cuando
se usa la sentencia while. Si un punto y coma es incorrectamente colocado al final de la sentencia
parcial while(contadorCiclo < 3);, como se muestra en el siguiente código, el ciclo también es
infinito. Este ciclo tiene un cuerpo vacı́o, o un cuerpo sin sentencias. Ası́, la expresión booleana
es evaluada, y como sea true, se ingresa al cuerpo del ciclo. Como el cuerpo del ciclo está vacı́o, no
se toma ninguna acción, y la expresión booleana es evaluada otra vez. Nada ha cambiado, ası́ que
sigue siendo true, se entra al cuerpo vacı́o, y el ciclo infinito continúa.
contadorCiclo = 1;
while (contadorCiclo < 3);
{
System.out.println("Hola");
contadorCiclo = contadorCiclo + 1;
}
Modificación de la variable de control del ciclo definido
Un ciclo definido es un ciclo controlado por contador porque la variable de control del ciclo es
cambiada contando. Es habitual modificar el valor de la variable de control del ciclo agregando uno
a esta, o incrementando la variable. Sin embargo, no todos los ciclos son controlados agregando
uno. El siguiente ciclo muestra “Hola” dos veces, pero el ciclo es controlando sustrayendo uno a la
variable de control de ciclo, o decrementándola.
contadorCiclo = 3;
4
while (contadorCiclo > 1) {
System.out.println("Hola");
contadorCiclo = contadorCiclo - 1;
}
En el segmento de programa previo, la variable contadorCiclo inicia con un tres. contadorCiclo
es mayor que uno, ası́ el cuerpo del ciclo muestra “Hola” y decrementa contadorCiclo a dos. La
expresión booleana en el ciclo while es revisada otra vez. Como dos es mayor que 1, “Hola” es
mostrado otra vez, y contadorCiclo se hace uno. Ahora contadorCiclo no es mayor que uno,
ası́ el ciclo termina. Hay varias formas de ejecutar un ciclo dos veces. Es importante no usar metodos
inusuales para contar repeticiones porque hacen al programa confuso; por ejemplo iniciar con doce,
continuar mientras sea mayor que dos, y decrementar el valor por cinco cada vez. El método más
claro y mejor es iniciar la variable de control del ciclo en cero o uno, incrementar por uno cada vez
a través del ciclo, y parar cuando la variable de control de ciclo alcance el lı́mite apropiado.
Nota. Muchos programadores expertos inician los valores de los contadores en cero porque estos
son usados cuando se trabaja con arreglos. En el caso de los arreglos sus elementos están numerados
o identificados con un ı́ndice iniciando con cero, ya que todos los elementos comparten el mismo
nombre.
Ciclo while indefinido
En ocasiones un ciclo es alterado por la entrada del usuario en vez de ser un ciclo controlado por
un contador, un ciclo indefinido es un ciclo controlado por evento, es decir, un evento ocurre
para determinar si el ciclo continúa. Un ciclo controlado por evento es un tipo de ciclo indefinido
porque no se sabe cuántas veces será repetido. Por ejemplo, quizás se quiera continuar haciendo
alguna tarea en tanto el usuario indique su deseo de continuar presionando alguna tecla, en este
caso no se sabe la cantidad de veces que será ejecutado el ciclo.
Considerar una aplicación en la cual se pide al usuario por un saldo bancario y enseguida se pide si
el usuario quiere ver el saldo después de que el interés ha sido acumulado. Cada vez que el usuario
decide continuar, un saldo incrementado aparece, mostrando un año más de interesés acumulados.
Cuando el usuario finalmente decide salir, el programa termina. El programa aparece en el código
1.
5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import j a v a . u t i l . Scanner ;
public c l a s s S a l d o B a n c a r i o {
public s t a t i c void main ( S t r i n g [ ] a r g s ) {
double s a l d o ;
int r e s p u e s t a ;
int año = 1 ;
f i n a l double TASA = 0 . 0 3 ;
Scanner t e c l a d o = new Scanner ( System . i n ) ;
System . out . p r i n t ( ” I n g r e s a r e l s a l d o b a n c a r i o i n i c i a l : ” ) ;
s a l d o = t e c l a d o . nextDouble ( ) ;
System . out . p r i n t l n ( ”¿ Q u i e r e s v e r e l s a l d o en e l s i g u i e n t e año ? ” ) ;
System . out . p r i n t ( ” I n g r e s a 1 para SI ” ) ;
System . out . p r i n t ( ” o c u a l q u i e r o t r o número para NO: ” ) ;
respuesta = teclado . nextInt ( ) ;
while ( r e s p u e s t a == 1 ) {
s a l d o = s a l d o + s a l d o ∗ TASA;
System . out . p r i n t l n ( ”\ t \ tDespué s d e l año ” + año + ” con ” +
(TASA∗ 1 0 0 ) + ” % de i n t e r és , ” +
” e l saldo es $” + saldo ) ;
año = año + 1 ;
System . out . p r i n t l n ( ”¿ Q u i e r e s v e r e l s a l d o a l f i n a l de o t r o año ? ” ) ;
System . out . p r i n t ( ” I n g r e s a 1 para SI ” ) ;
System . out . p r i n t ( ” o c u a l q u i e r o t r o número para NO: ” ) ;
respuesta = teclado . nextInt ( ) ;
}
}
}
Código 1: Aplicación SaldoBancario.
La aplicación del código 1 declara las variables necesarias y una constante para una tasa de interés
del 0.03, y luego pide al usuario el saldo. Enseguida la aplicación pide al usuario que ingresa un
uno si el usuario quiere ver saldo del siguiente año. Mientras el usuario quiera seguir continuando,
la aplicación seguirá mostrando los saldos bancarios incrementados.
El ciclo en la aplicación del código 1 inicia con la lı́nea que contiene:
while (respuesta == 1)
Si el usuario ingresa cualquier entero diferente de uno, el cuerpo del ciclo nunca se ejecuta y el
programa termina. Si el usuario ingresa uno, todas las sentencias dentro del cuerpo del ciclo se
ejecutan. La aplicación incrementa el saldo por la tasa de interés, muestra el nuevo saldo, agrega
uno al a~
no, y pregunta si el usuario quiere otro saldo. La última sentencia en el cuerpo del ciclo
acepta la respuesta del usuario. Después de la ejecución del cuerpo del ciclo, el control regresa a la
cima del ciclo, donde la expresión boolean en el ciclo while es probada nuevamente. Si el usuario
ingresa uno, se entra al ciclo y el proceso inicia nuevamente.
6
Validación de datos
Se emplean ciclos no definidos cuando se validan datos de entrada. La validación de datos es
el proceso para asegurar que un valor está dentro del rango deseado. Por ejemplo, suponer que se
requiere que el usuario meta un valor que no sea mayor que tres. El código 2 muestra una aplicación
que no continúa hasta que el usuario ingresa un valor correcto. Si el usuario ingresa tres o menos en
la primera petición, nunca se entra al ciclo. Pero, si el usuario ingresa un número mayor a tres, se
ejecuta el ciclo, dando otra oportunidad al usuario de ingresar el valor correcto. Mientras el usuario
dé datos incorrectos, el ciclo se repetirá.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import j a v a . u t i l . Scanner ;
public c l a s s MeteValorChico {
public s t a t i c void main ( S t r i n g [ ] a r g s ) {
int e n t r a d a U s u a r i o ;
f i n a l int LIMITE = 3 ;
Scanner t e c l a d o = new Scanner ( System . i n ) ;
System . out . p r i n t ( ” I n g r e s a r un e n t e r o que no s e a mayor a ” +
LIMITE + ” > ” ) ;
entradaUsuario = teclado . nextInt ( ) ;
while ( e n t r a d a U s u a r i o > LIMITE ) {
System . out . p r i n t l n ( ” El número i n g r e s a d o e s i n c o r r e c t o . ” ) ;
System . out . p r i n t ( ” I n g r e s a r un e n t e r o no mayor a ” +
LIMITE + ” > ” ) ;
entradaUsuario = teclado . nextInt ( ) ;
}
System . out . p r i n t l n ( ” Se i n g r e s ó c o r r e c t a m e n t e ” + e n t r a d a U s u a r i o ) ;
}
}
Código 2: La aplicación MeteValorChico.
Los programadores novatos cometen el error de revisar datos inválidos usando una decisión en vez
de un ciclo. Revisan si el dato es inválido usando una sentencia if; si el dato es inválido, lo vuelven
a pedir al usuario. Sin embargo, no consideran que el usuario podrı́a meter datos incorrectos más
de una vez.
Actividad 1. Escribir una aplicación que valide que un entero ingresado por el usuario sea 1, 2,
3, 5, 7 u 11.
3.
Operadores aritméticos abreviados
Incrementar una variable en un ciclo para llevar la cantidad de ocurrencias de algún evento es
conocido como contar. El proceso de repetidamente incrementar un valor por alguna cantidad es
conocida como acumular.
Java proporciona varios atajos para incrementar y acumular. La sentencia contar += 1; es idéntica
a contar = contar + 1;. El operador sumar y asignar es +=. La sentencia saldo += saldo
* TASA; incrementa el saldo por un porcentaje de la TASA. Otros atajos que se pueden usar son el
operador de sustraer y asignar (-=), operador de multiplicar y asignar (*=), operador
7
de dividir y asignar (/=), operador de residuo y asignar ( %=). Cada uno de estos operadores
es usado para hacer la operación y asignación del resultado en un paso.
Para incrementar el valor de una variable en uno, se pueden usar otros dos operadores de atajo—
el prefijo ++ también conocido como operador de incremento prefijo, y el postfijo ++, u
operador de incremento postfijo. Para usar un prefijo ++, se ponen dos signos de suma antes
del nombre de la variable. La sentencia algunValor = 6; seguida por ++algunValor; resulta en
algunValor teniendo siete. Para usar un postfijo ++, se ponen dos signos más justo después del
nombre de la variable. Enseguida se muestran cuatro formas para incrementar valor en uno; cada
uno da el mismo resultado. No es obligatorio emplear atajos, pero son una ventaja para escribir
menos código y tener menos errores.
int valor;
valor = 123;
++valor; // valor tiene 124
valor = 123;
valor++; // valor tiene 124
valor = 123;
valor = valor + 1; // valor tiene 124
valor = 123;
valor += 1; // valor tiene 124
El prefijo ++ y el postfijo ++ se usan con variables pero no con constantes. Es ilegal una expresión
como ++84;.
Nota. Los operadores de incremento prefijo y postfijo son operadores unarios, sólo se usan con una
variable. Otros operadores unarios son los operadores de conversión, o los operadores para indicar
valor positivo (+) y negativo (-).
Cuando sólo se quiere incrementar el valor de la variable en uno, no hay diferencia si se usa el
operador incremento prefijo o postfijo. Sin embargo, cuando un operador prefijo o postfijo es usado
como parte de una expresión, si hay diferencia en el resultado obtenido. Cuando un operador prefijo
es usado en una expresión, primero se incrementa la variable y después se evalúa la expresión, y
cuando un operador postfijo es usado primero se evalúa la expresión y luego se incrementa la
variable.
Considerar las siguientes sentencias:
b = 4;
c = ++b;
El resultado es que ambos b y c tienen el valor de cinco porque b es incrementado a cinco y luego
el valor de la expresión es asignado a c.
Cuando se usa el postfijo ++, el valor de la expresión antes del incremento es guardado. Considerar
la siguientes sentencias:
8
b = 4;
c = b++;
b tiene el valor de cinco, pero c es sólo cuatro.
La aplicación DemoIncremento, código 3, ilustra la diferencia de la forma como trabajan los operadores prefijo y postfijo. Cuando el operador incremento prefijo es usado en miNumero, este pasa de
123 a 124, y el resultado se asigna a respuesta, ası́ que tendrá 124. Luego de que el es puesto 123
en miNumero, el operador incremento postfijo es usado; 123 es asignado a respuesta, y miNumero
es incrementado a 124.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import j a v a . u t i l . Scanner ;
public c l a s s DemoIncremento {
public s t a t i c void main ( S t r i n g [ ] a r g s ) {
int miNumero , r e s p u e s t a ;
miNumero = 1 2 3 ;
System . out . p r i n t l n ( ” Antes de i n c r e m e n t a r , miNumero e s ” +
miNumero ) ;
r e s p u e s t a = ++miNumero ;
System . out . p r i n t l n ( ” Luego d e l i n c r e m e n t o p r e f i j o , miNumero e s ” +
miNumero ) ;
System . out . p r i n t l n ( ” y r e s p u e s t a e s ” + r e s p u e s t a ) ;
miNumero = 1 2 3 ;
System . out . p r i n t l n ( ” Antes de i n c r e m e n t a r , miNumero e s ” +
miNumero ) ;
r e s p u e s t a = miNumero++;
System . out . p r i n t l n ( ” Luego d e l i n c r e m e n t o p o s t f i j o , miNumero e s ” +
miNumero ) ;
System . out . p r i n t l n ( ” y r e s p u e s t a e s ” + r e s p u e s t a ) ;
}
}
Código 3: Aplicación DemoIncremento.
Escoger si se usa operador prefijo o postfijo es importante cuando es parte de una expresión más
grande. Por ejemplo, si d es 5, entonces 2 * ++d es 12, pero 2 * d++ es 10.
Una lógica similar se aplica cuando se usan los operadores decremento prefijo y postfijo. Por
ejemplo, si b = 4 y c = b--, 4 es asignado a c, pero b es decrementado y tiene el valor 3. Si b =
4 y c = --b, b es decrementado a 3 y 3 es asignado a c.
Actividad 2.
4.
Creación de ciclos for
Un ciclo for es un ciclo especial que es usado cuando una cantidad definida de iteraciones del ciclo
es requerida, ya que tiene una forma adecuada para crear un ciclo controlado por un contador. Un
ciclo while puede ser usado para lo anterior, sin embargo el ciclo for da una notación breve para
el ciclo definido. Al usar un ciclo for, se puede indicar el valor inicial para la variable de control del
ciclo, la condición de prueba que controla la entrada al ciclo, y la expresión que altera la variable
9
de control del ciclo.
El ciclo for inicia con la palabra reservada for seguido por un juego de paréntesis. Se puede usar una
sola sentencia o un bloque de sentencias como el cuerpo de un ciclo for. Muchos programadores
recomienden que siempre se use un juego de llaves para envolver el cuerpo de un ciclo for por
claridad.
Suponiendo que una variable llamada val ha sido declarada como un entero, entonces la sentencia
for mostrada a continuación da la misma salida que la sentencia while que está enseguida—ambas
muestran los enteros del 1 al 10.
for (val = 1; val < 11; ++val) {
System.out.println(val);
}
val = 1;
while (val < 11) {
System.out.println(val);
}
Dentro de los paréntesis de la sentencia for, del segmento de código anterior, la sección primera
antes de los primeros dos puntos inicializa val a uno. El programa ejecuta esta sentencia una sola
vez, sin importar cuantas veces el cuerpo del ciclo for se ejecuta.
Después de la inicialización, el control del programa pasa al centro, o sección de prueba, de la
sentencia for que está entre los dos puntos. Si la expresión booleana se evalúa a true, se entra al
cuerpo del ciclo for. Como val es puesta a uno, la expresión val < 11 es true. El cuerpo del ciclo
muestra val. Como el cuerpo del ciclo es una sola sentencia no se requieren las llaves.
Después que se ejecuta el cuerpo del ciclo, la sección tercera final del ciclo for que sigue al segundo
dos puntos se ejecuta, y val es incrementado a dos. El control del programa pasa de la sección
tercera a la sección segunda, donde val es comparada contra once una segunda vez. Como val es
todavı́a menor que once, se ejecuta el cuerpo: val (ahora teniendo dos) es mostrado, y luego la
tercera, que modifica una porción del ciclo for se ejecuta otra vez. La variable val se incrementa
a tres, y el ciclo for continúa.
Eventualmente, cuando val no es menor que once (después de que se mostró del uno al diez), el
ciclo for termina, y el programa continúa con la sentencia que siga al ciclo. Las tres secciones
del ciclo for son también conocidas como inicialización, prueba, e incremento, también se pueden
realizar las siguientes tareas:
Inicializar más de una variable en la primera sección de la sentencia for usando comas entre
estas, como sigue:
for (g = 0, h = 1; g < 6; ++g)
Realizar más de una prueba usando los operadores Y ó O en la segunda sección, por ejemplo:
for (g = 0; g < 3 && h > 1; ++g)
10
Decrementar o realizar alguna otra tarea en la tercera sección, por ejemplo:
for (g = 5; g >= 3; --g)
Alterar más de una variable en la tercera sección separándolas con comas, como sigue:
for (g = 0; g < 10; ++g, ++h, sum += g)
Se pueden dejar una o más secciones de un ciclo for vacı́as, sin embargo se requiere seguir
poniendo punto y coma. Suponiendo que x ha sido inicializada previamente, se podrı́a escribir:
for (; x < 10; ++x)
Dejar una sección, o varias, de una sentencia for vacı́a hace la lectura del código difı́cil.
Se sugiere usar la misma variable de control del ciclo en las tres partes de la sentencia for. También
deberı́a evitarse alterar la variable de control del ciclo en el cuerpo del ciclo, al hacer la modificación
en el cuerpo es más difı́cil seguir la lógica del programa, además puede generar errores complejos
de quitar.
Se podrı́a encontrar un ciclo for que no contenga cuerpo, como en el siguiente:
for (x = 0; x < 100000; ++x);
Este ciclo se usa para consumir tiempo de tal forma que se dé una breve pausa durante la ejecución
del programa. Java tiene un método nativo, sleep, para pausar la ejecución del programa. Este
método es parte de la clase Thread del paquete java.lang.
También se puede declarar una variable dentro de una sentencia for, como en la siguiente:
for (int val = 1; val < 11; ++val)
Actividad 3. Escribir una aplicación que muestre todos los números que sean divisores del 200.
5.
Creación de ciclos do...while
En los ejemplos revisados previamente, la variable de control del ciclo es evaluado en la “cima” del
ciclo antes de que el cuerpo tenga oportunidad de ejecutarse. Ambos ciclos while y for son ciclos
preprobados—aquellos en los cuales las variables de control del ciclo son probadas antes de que
el cuerpo del ciclo se ejecute.
En ocasiones se podrı́a necesitar asegurar que el cuerpo de un ciclo se ejecute al menos una vez.
Ası́ se quiere escribir un ciclo que revise en la “fondo” del ciclo después de la primera iteración. Tal
ciclo es el ciclo do...while; este es un ciclo postprobado—uno en el cual la variable de control
del ciclo es probado después de que se ejecuta el cuerpo del ciclo.
11
La figura 5 muestra la estructura general de un ciclo do...while. Observar que el cuerpo del ciclo se
ejecuta al menos una vez y antes de que se haga la pregunta de control del ciclo. El código 4 muestra
la aplicación SaldoBancario2 la cual contiene un ciclo do...while. El ciclo inicia en la lı́nea 11.
El cuerpo del ciclo está enseguida encerrada entre llaves. El saldo del primer año es mostrado antes
de que el usuario tenga oportunidad de responder. En el fondo del ciclo, se le pregunta al usuario
si quiere ver el saldo al final de otro año, ası́ el usuario tiene la opción de ver más saldos, pero ver
el primer mensaje no se puede.
Figura 2: Diagrama de flujo de un ciclo do...while.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import j a v a . u t i l . Scanner ;
public c l a s s S a l d o B a n c a r i o 2 {
public s t a t i c void main ( S t r i n g [ ] a r g s ) {
double s a l d o ;
int r e s p u e s t a ;
int año = 1 ;
f i n a l double TASA = 0 . 0 3 ;
Scanner t e c l a d o = new Scanner ( System . i n ) ;
System . out . p r i n t ( ” I n g r e s a r e l s a l d o b a n c a r i o i n i c i a l : ” ) ;
s a l d o = t e c l a d o . nextDouble ( ) ;
do {
s a l d o = s a l d o + s a l d o ∗ TASA;
System . out . p r i n t l n ( ”\ t \ tDespué s d e l año ” + año + ” con ” +
(TASA∗ 1 0 0 ) + ” % de i n t e r és , ” +
” e l saldo es $” + saldo ) ;
año = año + 1 ;
System . out . p r i n t l n ( ”¿ Q u i e r e s v e r e l s a l d o a l f i n a l de o t r o año ? ” ) ;
System . out . p r i n t ( ” I n g r e s a 1 para SI ” ) ;
System . out . p r i n t ( ” o c u a l q u i e r o t r o número para NO: ” ) ;
respuesta = teclado . nextInt ( ) ;
} while ( r e s p u e s t a == 1 ) ;
}
}
Código 4: La aplicación SaldoBancario2 usando el ciclo do...while.
12
Cuando el cuerpo de un ciclo do...while contiene una sola sentencia, no se requiere usar llaves.
El siguiente ciclo agrega valor a total mientras total permanezca menor que 200:
do
total += valor;
while (total < 200);
A pesar de que las llaves no son requeridas en este caso, varios programadores recomiendan usarlas,
de esta forma se previene que en la tercera lı́nea del código parezca que inicia un nuevo ciclo while
en vez de la finalización del ciclo do...while previo. El siguiente ejemplo incluye las llaves y es
menos probable de confundir al lector:
do {
total += valor;
} while (total < 200);
No es obligatorio usar el ciclo do...while. Pero cuando se sabe que se quiere hacer alguna tarea
al menos una vez, el ciclo do...while es más conveniente.
6.
Ciclos anidados
Al igual que la sentencia if que puede anidarse, también se puede con los ciclos. Cualquier tipo
de ciclo se puede poner dentro de cualquier otro tipo de ciclo. Cuando los ciclos están anidados,
cada par contiene un ciclo interno y un ciclo externo. El ciclo interno deberá estar enteramente
contenido dentro del ciclo externo; los ciclos no se pueden traslapar. Se puede anidar virtualmente
cualquier cantidad de ciclos; sin embargo, en algún punto, el sistema no podrá guardar toda la
información necesaria del ciclo.
Suponer que se quieren mostrar los saldos bancarios futuros mientras se varı́an los años y las tasas
de interés. El códigó 5 muestra una aplicación que contiene un ciclo externo que varia las tasas de
interés dentro de un rango. Al inicio del ciclo externo, el saldo es reiniciado a su valor inicial para
que los cálculos sean correctos para cada valor de interés revisado. El ciclo interno varı́a la cantidad
de años y muestra cada saldo calculado.
13
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import j a v a . u t i l . Scanner ;
public c l a s s S a l d o I n t e r e s A n u a l {
public s t a t i c void main ( S t r i n g [ ] a r g s ) {
double s a l d o I n i c i a l ;
double s a l d o ;
int año = 1 ;
double i n t e r e s ;
int r e s p u e s t a ;
f i n a l double BAJO = 0 . 0 2 ;
f i n a l double ALTO = 0 . 0 5 ;
f i n a l double INCREMENTO = 0 . 0 1 ;
f i n a l int MAX AÑOS = 4 ;
Scanner e n t r a d a = new Scanner ( System . i n ) ;
System . out . p r i n t ( ” I n g r e s a r e l s a l d o b a n c a r i o i n i c i a l : ” ) ;
s a l d o I n i c i a l = e n t r a d a . nextDouble ( ) ;
f o r ( i n t e r e s = BAJO; i n t e r e s <= ALTO; i n t e r e s += INCREMENTO) {
saldo = s a l d o I n i c i a l ;
System . out . p r i n t l n ( ”\nCon un s a l d o i n i c i a l de $ ” +
s a l d o + ” con una t a s a de i n t e r é s de ” + i n t e r e s ) ;
f o r ( año = 1 ; año <= MAX AÑOS ; ++año ) {
saldo = saldo + saldo ∗ interes ;
System . out . p r i n t l n ( ”Despué s d e l año ” + año +
” saldo es $” + saldo ) ;
}
}
}
}
Código 5: La clase SaldoInteresAnual contiene ciclos anidados.
Cuando se anidan ciclos, en ocasiones no hay ninguna diferencia cual variable controla el ciclo
externo y cual el interno, pero en ocasiones sı́ hay diferencia. Con ciclo anidados se debe pensar
en el ciclo externo como el ciclo que abarca. La variable en el ciclo externo cambia con menor
frecuencia. Suponer que se tiene un método imprimirEtiqueta que hace etiquetas de correo personalizadas con tres diferentes colores para hacer propaganda. Los siguientes ciclos anidados llaman
al método 60 veces y producen tres etiquetas para el primer cliente, tres etiquetas para el segundo,
y ası́ sucesivamente:
for (cliente = 1; cliente <= 20; ++cliente)
for (color = 1; color <= 3; ++color)
imprimirEtiqueta();
Los siguientes ciclos anidados también llaman 60 veces a imprimirEtiqueta, y generan las mismas
60 etiquetas, pero crean primero 20 etiquetas con el primer color, 20 etiquetas del segundo color, y
finalmente 20 etiquetas del tercer color.
for (color = 1; color <= 3; ++color)
for (cliente = 1; cliente <= 20; ++cliente)
imprimirEtiqueta();
14
Si cambiar el cartucho de color es un proceso consumidor de tiempo que sucede en el método,
entonces el último ejemplo se ejecutará más rápido que el ejemplo que le antecede.
Actividad 4. Escribir una aplicación que genere las tablas de multiplicar a partir del 91 y hasta
el 100. Cada una de las tablas de multiplicar irá desde 0 hasta 20, ası́ entonces la primera tabla
será desde 91 x 0, 91 x 1, . . . ,91 x 20, luego seguirı́a la del 92, y ası́ hasta llegar a la del 100.
7.
Rendimiento de ciclos
Ya sea que se use un ciclo while, for, o do...while, se puede mejorar el rendimiento del ciclo
considerando lo siguiente:
Asegurarse que el ciclo no incluya operaciones o sentencias innecesarias.
Considerar el orden de evaluación para los operadores cortocircuitados.
Hacer comparaciones con cero.
Emplear fusiones de ciclos.
Usar incremento prefijo en vez de incremento postfijo.
Evitar operaciones innecesarias
Se pueden hacer los ciclos más eficientes no haciendo operaciones o sentencias innecesarias, ya sea
en la expresión de prueba del ciclo o dentro del cuerpo del ciclo. Por ejemplo, suponer un ciclo que
se debe ejecutar mientras x es menor que la suma de dos enteros, a y b. El ciclo se podrı́a escribir
como:
while (x < a + b)
// cuerpo del ciclo
Si el ciclo se ejecuta 1,000 veces, entonces la expresión a + b también es calculado 1,000 veces. Se
puede usar el siguiente código, con el mismo resultado, pero la suma es hecha una sola vez:
int suma = a + b;
while (x < suma)
// cuerpo del ciclo
Este código es más eficiente siempre y cuando a y b no sean alteradas en el cuerpo del ciclo, porque
sino la suma se debe hacer en cada iteración del ciclo.
De igual forma se debe evitar llamar a un método dentro de un ciclo si es posible. Por ejemplo,
si el método getNumeroDeEmpleados() siempre regresa el mismo valor durante la ejecución del
programa, entonces un ciclo como el siguiente estarı́a haciendo muchas llamadas innecesarias:
15
while (contador < getNumeroDeEmpleados())...
Por lo que es más eficiente llamar al método una sola vez, dejando el resultado en una variable, y
usar la variable en la prueba del ciclo, como sigue:
int numEmpleados = getNumeroDeEmpleados();
while (contador < numEmpleados)...
Orden de evaluación en operadores cortocircuitados
Los operadores lógicos Y y O usan evaluación cortocircuitada, es decir, son evaluados tanto como
sea necesario para determinar si la expresión es true o false.
Suponer que se requiere pedir a un usuario una cantidad de copias de un reporte entre 0 y 15, y
validar la entrada del usuario antes de proceder. Si la probabilidad de que el usuario dé un valor
muy grande, es mayor respecto a la probabilidad de que dé un numero negativo, el ciclo que pide
nuevamente al usuario la cantidad de copias se deberá escribir como:
while (cantidadSolicitada > LIMITE || cantidadSolicitada < 0)...
Comparaciones con cero
Hacer comparaciones con cero es más rápido que hacer comparaciones contra cualquier otro valor.
Por lo tanto, si en la aplicación hacer la comparación con cero es factible, se puede mejorar el
rendimiento del ciclo estructurando los ciclos para comparar la variable de control del ciclo con
cero en vez de cualquier otro valor. Por ejemplo, un ciclo que trabaje con una variable que varı́e
de 0 hasta 100,000, se ejecuta el mismo número de veces como un ciclo en una variable que varı́e
desde 100,000 hasta 0, pero este segundo ciclo termina más pronto.
Fusiones de ciclos
La fusión de ciclos es la técnica de combinar dos ciclos en uno. Por ejemplo, suponer que se quiere
llamar dos métodos cada uno 100 veces. Se puede poner una constante con nombre VECES a 100 y
usar el siguiente código:
for (int x = 0; x < VECES; ++x)
metodo1();
for (int x = 0; x < VECES; ++x)
metodo2();
Sin embargo también se puede usar el siguiente código:
16
for (int x = 0; x < VECES; ++x) {
metodo1();
metodo2();
}
La fusión de ciclos no siempre se puede realizar, en ocasiones todas las actividades del metodo1()
deberán ser terminadas antes que la del metodo2(). Por otra parte, si se logra terminar con unos
cuantos milisegundos haciendo el código más difı́cil de entender, se deberá preferir un programa
más lento pero más leı́ble.
17
Descargar