Ejercicios resueltos laboratorio 9 - Descomposición en funciones

Asignatura: Fundamentos de Informática
Especialidad: Electrónica - UPV/EHU
Curso académico: 2013-2014
Profesor: Ismael Etxeberria Agiriano

Como ejercicio resuelto vamos a retomar el problema de decir si un número es primo, visto en el capítulo anterior, ya que en este laboratorio se trata de organizar los programas que ya sabíamos hacer en funciones.

Enunciado del ejercicio: 09-pri

Diseña y codifica un programa que lea un número n mayor que 1 y diga si n es primo. Si el número no es válido escribirá un mensaje de error. Al finalizar preguntará si se quiere volver a repetir la operación.
Utiliza una función que calcule si el número es primo.

Resolución 09-pri-1

Centraremos la descripción en los aspectos propios de la descomposición en funciones: la especificación de los prototipos, el paso de parámetros, la llamada a las funciones y la devolución de los valores.

En la descomposición del problema vamos a proponer la utilización de la función EsPrimo que determinará si el número pasado como parámetro es primo o no. Su cabecera se muestra en la figura 9.1.


Figura 9.1. Cabecera de la función EsPrimo.

En la figura 9.2 se propone el diagrama de flujo del programa principal de la variante 09-pri-1 que hace uso de la función EsPrimo propuesta, dentro de una instrucción condicional. Se podrá reconocer la estructura general de la variante 08-002-1 vista en el ejercicio resuelto del capítulo anterior.


Figura 9.2. Programa principal de la variante 09-pri-1.

Para representar en el diagrama de flujo el valor devuelto por la función, el valor de la variable primo en nuestro caso, utilizaremos la convención de asignarle ese valor al nombre de la función, como se muestra en la Figura 9.3.


Figura 9.3. Valor devuelto por una función.

Nótese que siempre que una función devuelve un valor (mediante la instrucción return) ésta termina por lo que siempre irá seguida del bloque etiquetado Fin.

El diagrama de flujo de la función EsPrimo (Figura 9.4) tendrá la cabecera propuesta en la Figura 9.1 y el cuerpo de ésta, similar a lo que hacíamos en el capítulo anterior.


Figura 9.4. Variante 1 de la función EsPrimo.

Nótese que se ha optado por llamar de la misma manera a la variable que alberga el número en cuestión n tanto en el programa principal, la función main (en la que es una variable local) y en la función EsPrimo (en la que es un parámetro formal).

Otro aspecto a tener en cuenta es que se trata de un diseño descendente: primero especificaremos el programa principal (main) y luego las funciones creadas por nosotros llamadas por main, y así sucesivamente.

Para evitar posibles problemas de compilación es conveniente proporcionar los prototipos de todas las funciones que se declararán después.

La codificación correspondiente a la variante 1 podría ser:


/* 09-pri-1.c (versión laboratorio)
 *
 * Lee un número positivo n y dice si es primo.
 * Si el número no es positivo escribe un mensaje de error.
 * Al finalizar preguntará si se quiere repetir la operación.
 *
 * Utiliza una función que calcule si el número es primo.
 *
 * Variante 1. Utilizando while. Variable lógica Cierto y Falso.
 */
#include <stdio.h>
#include <stdlib.h>

int EsPrimo (int num); /* Prototipo */

int main (void)
{
  int i, n;
  char op;

  do {
    printf ("Introduce un número mayor de uno: ");
    scanf ("%d", &n);
    if (n > 1)
      if (EsPrimo(n)) 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;
}

#define Cierto 1
#define Falso 0

int EsPrimo (int num)
{
  int i;
  int primo; /* Variable lógica que guarda si es primo o no */

  primo = Cierto; /* Mientras no encontremos un un divisor es primo */
  i = 2;
  while (i < num && primo) {
    if (num%i == 0)
      primo = Falso; /* Si tiene un divisor no es primo */
    i++;
  }
  return primo; /* Devuelve resultado: primo */
}

Resolución 09-pri-2

Aunque podríamos hacer múltiples variantes respecto al programa principal, en lo que respecta al diagrama de flujo vamos a centrarnos en lo que puede cambiar sustancialmente frente a lo visto en el capítulo anterior.

La variante 08-002-2 del capítulo anterior presentaba el resultado mediante una única instrucción printf. En esta segunda variante hemos hecho lo mismo, aunque ahora en vez de la variable lógica hemos introducido directamente la llamada a la función EsPrimo.

El resto del código es similar a la variante correspondiente 08-002-2.

Recalcaremos que la utilización de estructuras for en contextos como éste en el que no conocemos a priori el número de veces que se va a ejecutar el código no es muy ortodoxo en el ámbito de la programación estructurada, aunque es típico entre programadores C.

La codificación asociada podría ser:


/* 09-pri-2.c (versión laboratorio)
 *
 * Lee un número positivo n y dice si es primo.
 * Si el número no es positivo escribe un mensaje de error.
 * Al finalizar preguntará si se quiere repetir la operación.
 *
 * Utiliza una función que calcule si el número es primo.
 *
 * Variante 2. Utilizando for y break. Variable lógica 0 y 1.
 */
#include <stdio.h>
#include <stdlib.h>

int EsPrimo (int num); /* Prototipo */

int main (void)
{
  int i, n;
  char op;

  do {
    printf ("Introduce un número mayor de uno: ");
    scanf ("%d", &n);
    if (n > 1)
      printf ("%d %ses primo\n", n, EsPrimo(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;
}

int EsPrimo (int n)
{
  int i;
  int primo; /* Variable lógica que guarda si es primo o no */

  primo = 1;
  for (i = 2; i < n; i++) {
    if (n%i == 0) {
      primo = 0;
      break;
    }
  }
  return primo;
}

Resoluciones 09-pri-3, 09-pri-4 y 09-pri-5

En este apartado se proporciona el código de las variantes 09-pri-3, 09-pri-4 y 09-pri-5, que se corresponden con sus respectivas variantes 08-002-3, 08-002-4 y 08-002-5 utilizando funciones.

Sólo se proporciona el código correspondiente a la función EsPrimo.

El código de la variante 09-pri-3 puede ser:


/* 09-pri-3.c (versión laboratorio)
...

int EsPrimo (int n)
{
  int i;

  for (i = 2; i < n; i++)
    if (n%i == 0)
      break;
  return i == n;
}

El código de la variante 09-pri-4 puede ser:


/* 09-pri-4.c (versión laboratorio)
...
int EsPrimo (int n)
{
  int i;

  i = 2;
  while (i<n && n%i)
    i++;

  return i == n;
}

El código de la variante 09-pri-5 puede ser:


/* 09-pri-5.c (versión laboratorio)
...
int EsPrimo (int n)
{
  int i;

  for (i = 2; i<n && n%i; i++)
    ; /* Ojo: sin cuerpo */

  return i == n;
}

Resolución 09-pri-6

A lo largo de este ejercicio resuelto hemos ido proponiendo variantes similares a las vistas en el capítulo anterior utilizando funciones.

Una variante típica cuando se utilizan funciones es finalizar la función y devolver el resultado cuando ya está disponible.

En muchos lenguajes de programación podemos utilizar el nombre de la función como una variable más y ello no supone una terminación. Si queremos representar explícitamente en el diagrama de flujo que vamos a utilizar una ruptura de secuencia utilizaremos un símbolo especial, que es el que se muestra en la Figura 9.5. Mostramos con líneas discontinuas el flujo normal que seguiría en el caso de que no hubiera una ruptura de secuencia.


Figura 9.5. Valor devuelto por una función con ruptura.

En la figura 9.6 se muestra el diagrama de flujo de la función EsPrimo utilizando esta práctica, es decir, devolver el resultado cuando ya está disponible y finalizar. Nótese que hay un caso normal en el que no se explota la ruptura de secuencia.


Figura 9.6. Función EsPrimo devolviendo el resultado tan pronto como es conocido.

El código de esta variante 09-pri-6 puede ser:


/* 09-pri-6.c (versión laboratorio)
...
int EsPrimo (int n)
{
  int i;

  for (i = 2; i < n; i++)
    if (n%i == 0)
      return 0; /* Si tiene un divisor no es primo */
  return 1; /* No tiene ningun divisor: es primo */
}