Asignatura: Fundamentos de Informática | ![]() |
![]() |
Especialidad: Electrónica - UPV/EHU | ||
Curso académico: 2013-2014 | ||
Profesor: Ismael Etxeberria Agiriano |
Este ejercicio es similar al ejercicio resuelto 07-001 del laboratorio anterior salvo que:
while
.
Ahora podemos elegir la estructura iterativa y
utilizar la más natural.
Partiendo de la solución anterior,
se supone que vamos a querer ejecutar
al menos una vez el programa anterior, o lo que es lo mismo,
escribir al menos una vez n
líneas con la palabra "Hola",
por lo que podemos decir que se intuye una estructura
do-while
para hacerlo una o más veces.
Cada vez que terminemos de mostrar el texto "Hola" las veces que sea preciso
preguntaremos si el usuario desea volver a repetir el proceso.
Este bloque se representa en gris claro en la Figura 8.1.
El bloque de color amarillo pálido es el mismo que vimos en la
Figura 7.1 del laboratorio anterior,
con la expresión condicional de verificación
y el bloque while
.
El diagrama de flujo de esta variante sería:
while
La codificación correspondiente a la variante 1 podría ser:
/* 08-001-1.c (versión laboratorio) * * Leer un número positivo n y escribir la palabra "Hola" n veces. * Si el número no es positivo escribir mensaje de error. * Al finalizar preguntará si se quiere repetir la operación. * * Variante 1. Utilizando while. */ #include <stdio.h> #include <stdlib.h> int main (void) { int i, n; char op; do { printf ("Introduce un número positivo: "); scanf ("%d", &n); if (n >= 0) { i = 1; while (i <= n) { printf ("Hola\n"); i++; } } else printf ("%d no es un número positivo\n", n); printf ("¿Quieres repetir? (s/n): "); fflush (stdin); /* Vaciar la entrada, si no queda el carácter '\n' */ scanf ("%c", &op); } while (op == 's' || op == 'S'); system ("pause"); return 0; }
El segundo bloque de lectura (Lee op
)
para leer el carácter de la respuesta ('s'
o 'S'
)
se divide en tres instrucciones C que volvemos a reproducir a
continuación:
printf ("¿Quieres repetir? (s/n): "); fflush (stdin); /* Vaciar la entrada, si no queda el carácter '\n' */ scanf ("%c", &op);
La novedad aquí es la introducción de la llamada fflush()
.
de stdio.h
.
Suele suceder a menudo que tras utilizar la función
scanf()
queda pendiente de leer el fin de línea
('\n'
)
que valida la entrada de datos.
Para evitar que la siguiente llamada a scanf()
en busca de un carácter
lea ese fin de línea, lo típico es llamar a la función
fflush(stdin)
que vacía la
entrada de datos del programa (stdin
) y nos asegura que el carácter
leído viene después de pedirlo y no antes, al leer la variable n
.
Prueba a quitar la línea fflush()
y comprobarás que
el programa no se detiene a preguntar nada.
Ya que vimos el ejercicio 07-001 del laboratorio anterior
utilizando una estructura while
vamos a ver primero este mismo ejercicio con una variante
for
.
for
Su codificación correspondiente:
/* 07-001-2.c (versión laboratorio) * * Leer un número positivo n y escribir la palabra "Hola" n veces. * Si el número no es positivo escribir mensaje de error. * * Variante 2. Utilizando for. */ #include <stdio.h> #include <stdlib.h> int main (void) { int i, n; printf ("Introduce un número positivo: "); scanf ("%d", &n); if (n >= 0) for (i = 1; i <= n; i++) printf ("Hola\n"); else printf ("%d no es un número positivo\n", n); system ("pause"); return 0; }
Si combinamos la resolución 08-001-1, que utiliza
while
con la resolución 07-001-2, que utiliza
for
obtenemos una variante más apropiada.
Se observa que prácticamente el diagrama de flujo es idéntico si ignoramos los
colores.
Mediante éstos se explicita que el bloque de inicialización y el bloque de
actualización forman parte de sus respectivas cláusulas de la estructura
for
.
No es de extrañar la idoneidad del for
para este ejercicio
ya que sabemos de antemano cuántas veces hemos de escribir la palabra "Hola".
El diagrama de flujo sería:
for
Merece la pena prestar especial atención a la transformación que se produce en el código.
La codificación correspondiente a la variante 2 podría ser:
/* 08-001-2.c (versión laboratorio) * * Leer un número positivo n y escribir la palabra "Hola" n veces. * Si el número no es positivo escribir mensaje de error. * Al finalizar preguntará si se quiere repetir la operación. * * Variante 2. Utilizando for. */ #include <stdio.h> #include <stdlib.h> int main (void) { int i, n; char op; do { printf ("Introduce un número positivo: "); scanf ("%d", &n); if (n >= 0) for (i = 1; i <= n; i++) printf ("Hola\n"); else printf ("%d no es un número positivo\n", n); printf ("¿Quieres repetir? (s/n): "); fflush (stdin); /* Vaciar la entrada, si no queda el carácter '\n' */ scanf ("%c", &op); } while (op == 's' || op == 'S'); system ("pause"); return 0; }
La estructura do-while
para repetir el proceso una y otra
vez ya la hemos visto en el ejemplo anterior por lo que no vamos a detallar su
descripción.
Para determinar si un número es primo tenemos que buscar un divisor, es decir, un número que lo divida de manera exacta o, lo que es lo mismo, que la división entera por ese número dé 0. Si encontramos un divisor no será primo y si agotamos todas las posibilidades sin encontrar un divisor entonces sabremos que es primo.
Vamos a utilizar una variable booleana primo
para memorizar si el número en cuestión es primo o no.
En C no hay variables booleanas, pero eso no nos importa cuando estamos
escribiendo el algoritmo.
Mientras no encontremos un divisor podemos decir que el número es primo, por
lo que inicializamos primo
a Cierto.
Dicho de otra manera, un número es primo mientras no se demuestre lo contrario.
Iremos comprobando uno a uno todos los posibles divisores entre 2
y n-1
.
Si encontramos uno actualizaremos el valor de primo
a Falso.
En ese caso no queremos seguir, por lo que no sabemos a priori
cuántas veces vamos a ejecutar el cuerpo del bucle.
Por ello, como la mayoría de los algoritmos de búsqueda, la estructura
iterativa más adecuada será el while
.
El diagrama de flujo puede ser el mostrado en la Figura 8.4.
while
y primo
Se recomienda fijar la atención para distinguir las instrucciones condicionales de las iterativas ya que, en papel, no se distinguen los colores.
En C no existen las constantes booleanas Cierto y Falso por lo que tendremos que definirlas. En el programa hemos utilizado macrodefiniciones (#define) para este fin. Así, toda ocurrencia de Cierto en el programa se susituye por 1 y toda ocurrencia de Falso por 0.
La codificación correspondiente a la variante 1 podría ser:
/* 08-002-1.c (versión laboratorio) * * Leer un número positivo n y decir si es primo. * Si el número no es positivo escribir mensaje de error. * Al finalizar preguntará si se quiere repetir la operación. * * Variante 1. Utilizando while. Variable booleana Cierto y Falso. */ #include <stdio.h> #include <stdlib.h> #define Cierto 1 #define Falso 0 int main (void) { int i, n; int primo; char op; do { printf ("Introduce un número mayor de uno: "); scanf ("%d", &n); if (n > 1) { primo = Cierto; i = 2; while (i < n && primo) { if (n%i == 0) primo = Falso; i++; } if (primo) printf ("%d es primo\n", n); else printf ("%d no es primo\n", n); } else printf ("%d no es un número positivo\n", n); printf ("¿Quieres repetir? (s/n): "); fflush (stdin); /* Vaciar la entrada, si no queda el carácter '\n' */ scanf ("%c", &op); } while (op == 's' || op == 'S'); system ("pause"); return 0; }
En esta variante vamos a realizar tres alteraciones:
for
con rupturas de secuencia
La utilización de estructuras for
para algoritmos de
búsqueda como éste tiene partidarios y detractores por lo que vamos a darles
igual validez a unos y a otros.
Desde el punto de vista de la programación estructurada no es correcto ya que no se corresponde con el esquema teórico de conocer de antemano el número de iteraciones. Se recomienda no utilizarlo pero siempre nos encontraremos quien lo haga.
La idea de utilizar un estructura for
, como se verá en el
diagrama de flujo, consiste en suponer que vamos a ejecutar el algoritmo un
número máximo de veces n, y si encontramos lo que queremos saldremos
directamente mediante una orden break
.
Los puristas alegan que las rupturas del flujo de control
(break
,
continue
,
goto
y
return
indiscriminados) complican la comprensión del
programa.
En el diagrama resultante de la figura 8.2 se observa que ya no tenemos que
mirar en la condición iterativa si ha dejado de ser primo ya que en ese caso
salimos directamente mediante una ruptura de secuencia
break
.
El diagrama de flujo propuesto es:
for
La utilización de una sola instrucción de escritura está ligada al lenguaje. Se calcula si vamos a añadir la cadena "no " si de da que el número no es primo. Analícese el código correspondiente.
La codificación correspondiente a la variante 2 podría ser:
/* 08-002-2.c (versión laboratorio) * * Leer un número positivo n y decir si es primo. * Si el número no es positivo escribir mensaje de error. * Al finalizar preguntará si se quiere repetir la operación. * * Variante 2. Utilizando for y break. Variable booleana 0 y 1. */ #include <stdio.h> #include <stdlib.h> int main (void) { int i, n; int primo; char op; do { printf ("Introduce un número mayor de uno: "); scanf ("%d", &n); if (n > 1) { primo = 1; for (i = 2; i < n; i++) { if (n%i == 0) { primo = 0; break; } } printf ("%d %ses primo\n", n, primo? "":"no "); } else printf ("%d no es un número positivo\n", n); printf ("¿Quieres repetir? (s/n): "); fflush (stdin); /* Vaciar la entrada, si no queda el carácter '\n' */ scanf ("%c", &op); } while (op == 's' || op == 'S'); system ("pause"); return 0; }
En esta última resolución completa vamos a prescindir de la variable booleana
primo
.
En realidad podemos saber si el número es primo mirando cómo hemos salido del
bucle.
Si el contador ha llegado a su valor máximo quiere decir que hemos agotado
todas las posibilidades, es decir, que el número es primo.
for
Se ha querido explicitar que la condición de salida determina si es primo o no en el algoritmo a la hora de escribir el resultado, mostrándose una estructura condicional con dos escrituras. Veremos que en el código hemos utilizado una única instrucción de escritura para simplificar el programa.
/* 08-002-3.c (versión laboratorio) * * Leer un número positivo n y decir si es primo. * Si el número no es positivo escribir mensaje de error. * Al finalizar preguntará si se quiere repetir la operación. * * Variante 3. Utilizando for y break. Sin variable booleana. */ #include <stdio.h> #include <stdlib.h> int main (void) { int i, n; char op; do { printf ("Introduce un número mayor de uno: "); scanf ("%d", &n); if (n > 1) { for (i = 2; i < n; i++) if (n%i == 0) break; printf ("%d %ses primo\n", n, i==n? "":"no "); } else printf ("%d no es un número positivo\n", n); printf ("¿Quieres repetir? (s/n): "); fflush (stdin); /* Vaciar la entrada, si no queda el carácter '\n' */ scanf ("%c", &op); } while (op == 's' || op == 'S'); system ("pause"); return 0; }
Podemos añadir dos nuevas variantes sin utilizar rupturas de secuencia.
La primera con while
que no utiliza variables booleaas:
/* 08-002-4.c (versión laboratorio) ... if (n > 1) { i = 2; while (i < n && n%i) i++; printf ("%d %ses primo\n", n, i==n? "":"no "); } else ...
La segunda con for
, pero en un modo falso
ya que tiene función while
,
en cuanto a que no sabemos de antemano el número de iteraciones.
El código será (cuidado, que no tiene cuerpo):
/* 08-002-5.c ... if (n > 1) { for (i = 2; i < n && n%i; i++) ; /* Ojo: sin cuerpo */ printf ("%d %ses primo\n", n, i==n? "":"no "); } else ...