1. Entrada y salida

1.1. Leer horas, minutos, segundos

Escriba un programa que lea una hora en formato h:m:s y la imprima con ceros a la izquierda.

Listado 1. Lee horas, minutos y segundos separados por dos puntos (en C)
#include <stdio.h>

// procedure main()
int main() {
  unsigned short hour = 0, min = 0, sec = 0;
  //scanf("%hu:%hu:%hu", &hour, &min, &sec);
  printf("hour %hu:%hu:%hu\n", hour, min, sec);
}

2. Expresiones y condicionales

Escriba una expresión que resuelva cada una de las siguientes situaciones.

2.1. Números aleatorios en rango

Suponga que rand() genera un número aleatorio entero en el rango {0, …​, r - 1}, donde normalmente r = 2^31. Escriba una expresión/función que siempre genere un número aleatorio de lotería, es decir, entre {00, …​, 99}.

Listado 2. Genera un número aleatorio entero en el rango [0, 100[ (en C)
int rand100() {
  return rand() % 100;
}

Escriba una expresión/función que genere un número aleatorio entero entre {a, …​, b - 1}.

2.1.1. Diseño por contratos

Versión 1. Diseño por contratos (design by contract). Indicar en la documentación (comentarios) que es responsabilidad de quien usa la función, proveer los valores correctos.

Listado 3. Versión 1: quien usa la función debe proveer dos enteros que cumpla min < max (en C)
/**
 * @brief Return a random number in range [min, max[.
 * @param min The minimum number
 * @param max The maximum
 * @remark min must be less than max, otherwise the function generates
 * invalid data or may crash the process
 * @return A random number in range [min, max[.
 */
int rand_between(const int min, const int max) {
  return min + rand() % (max - min);
}

2.1.2. Assert

Versión 2. Programación defensiva (defensive programming) con diseño por contratos. La subrutina se defiende ante situaciones anómalas o problemáticas pero sólo advierte a desarrolladores, no usuarios. La expresión assert()` evalúa una condición, si esta es falsa, detiene el programa y reporta un error para el equipo de desarrollo en lo que se conoce como "versión debug" del programa. En la versión para los usuarios finales (release), los assert() son removidos (como si nunca hubieran estado en el código fuente) porque no tiene sentido que un supuesto se dispare a quien no tiene responsabilidad del código fuente.

Listado 4. Versión 2: Programación defensiva que verifica un supuesto sólo en la versión de desarrollo (en C)
/**
 * @brief Return a random number in range [min, max[.
 * @param min The minimum number
 * @param max The maximum
 * @remark min must be less than max, otherwise the function generates
 * invalid data
 * @return A random number in range [min, max[.
 */
int rand_between(int min, int max) {
  assert(min < max);
  return min + rand() % (max - min);
}

2.1.3. Programación defensiva

Versión 3. Programación defensiva (defensive programming). La subrutina se defiende ante situaciones anómalas o problemáticas con condicionaes if () que verifica los supuestos y se defiende de errores de desarrolladores y usuarios con reacciones como retornar códigos de error, avisa de errores en alguna memoria compartida, lanza excepciones, o imprime mensajes de error. Esta es la versión que debe usarse si los datos provienen de usuarios humanos, otros programas, o fuentes no confiables.

Listado 5. Versión 3: Programación defensiva que verifica un supuesto tanto en la versión de desarrollo como la versión release (en C)
/**
 * @brief Return a random number in range [min, max[.
 * @param min The minimum number
 * @param max The maximum
 * @remark If min >= max, raises an exception
 * @return A random number in range [min, max[.
 */
int rand_between(int min, int max) {
  if (min < max) {
    return min + rand() % (max - min);
  } else {
    // Return an error-code
    // Set a global variable with error code
    // Raise/throw an exception
    // Print an error message
  }
}

2.1.4. Rango aleatorio en Java

Adapte las expresiones de los dos ejercicios anteriores, si el número aleatorio es un flotante en el rango [0, 1[.

Listado 6. En Java, Math.rand() cada vez que se invoca, genera un flotante al azar en el rango [0, 1]
public int randBetween(int min, int max) {
  if (min < max) {
    return min + (int) (Math.random() * (max - min));
  } else {
    // TODO(you): fix
  }
}

2.2. Días después

Si hoy es domingo, qué día de la semana es dentro de x días. Por ejemplo, para x=1 sería lunes, x=2 sería martes, x=7 sería domingo, x=10 sería miércoles. Escriba una función que recibe x y retorne el número de día. Escriba otra que recibe un número de día y retorne un texto con el nombre del día.

Listado 7. Primera función que indica con un entero entre 0 y 6 el día de la semana
public int daysAfter(int x) {
  assert x >= 0;
  return x % 7;
}
Listado 8. Segunda función que mapea el número del día a un texto usando if/else
public String nameDaysAfter(int x) {
  String result = "";
  if (daysAfter(x) == 0) {
    result = "domingo";
  } else if (daysAfter(x) == 1) {
    result = "lunes";
  } else if (daysAfter(x) == 2) {
    result = "martes";
  } else if (daysAfter(x) == 3) {
    result = "miercoles";
  } else if (daysAfter(x) == 4) {
    result = "jueves";
  } else if (daysAfter(x) == 5) {
    result = "viernes";
  } else if (daysAfter(x) == 6) {
    result = "sabado";
  } else {
    assert false;
  }
  return result;
}
Listado 9. Segunda función que mapea el número del día a un texto usando switch
public String nameDaysAfter(int x) {
  String result = "";
  switch (daysAfter(x)) {
    case 0: result = "domingo"; break;
    case 1: result = "lunes"; break;
    case 2: result = "martes"; break;
    case 3: result = "miercoles"; break;
    case 4: result = "jueves"; break;
    case 5: result = "viernes"; break;
    case 6: result = "sabado"; break;
    default: assert false; break;
  }
  return result;
}
Listado 10. Tercera función que mapea el número del día a un texto usando if y return
public String nameDaysAfter(int x) {
  if (daysAfter(x) == 0) return "domingo";
  if (daysAfter(x) == 1) return "lunes";
  if (daysAfter(x) == 2) return "martes";
  if (daysAfter(x) == 3) return "miercoles";
  if (daysAfter(x) == 4) return "jueves";
  if (daysAfter(x) == 5) return "viernes";
  if (daysAfter(x) == 6) return "sabado";
  assert false;
}
Listado 11. Cuarta función que mapea el número del día a un texto usando switch y return
public String nameDaysAfter(int x) {
  switch (daysAfter(x)) {
    case 0: return "domingo";
    case 1: return "lunes";
    case 2: return "martes";
    case 3: return "miercoles";
    case 4: return "jueves";
    case 5: return "viernes";
    case 6: return "sabado";
    default: assert false; break;
  }
}

2.3. Tablero de bingo

Cuando el organizador de un juego de Bingo saca una bola de la tómbola, debe colocarla en una mesa que tiene 5 filas, una por cada letra de la palabra BINGO. Cada fila tiene 15 agujeros. Cuando sale el número x de la tómbola, ¿en qué letra (fila) y agujero debe colocarla el organizador? Escriba un procedimiento que recibe el número de la bola e imprime en la salida estándar la letra de la fila y el número del agujero donde debe colocarse.

Listado 12. Función que indica la columna en que debe colocarse la bola cuyo número está entre 1 y 75 inclusive
public int bingoColumn(int ball) {
  assert ball >= 1 && ball <= 75;
  return (ball - 1) % 15 + 1;
}
Listado 13. Función que indica con un entero la fila en que debe colocarse la bola cuyo número está entre 1 y 75 inclusive
public int bingoRow(int ball) {
  assert ball >= 1 && ball <= 75;
  return (ball - 1) / 15 + 1;
}
Listado 14. Función que indica con una letra ('B', 'I', 'N', 'G', 'O') la fila en que debe colocarse la bola cuyo número está entre 1 y 75 inclusive
public char bingoRowLetter(int ball) {
  int index = bingoRow(ball);
  assert index >= 1 && index <= 5;
  return "BINGO".charAt(index - 1);
}

2.4. El ventanal roto

Un ventanal de un templo de 2m de ancho por 3m de alto, esta construido con varios vidrios cuadrados de 50cm de lado, dispuestos matricialmente. Suponga que el inicio es la esquina superior izquierda. El templo está frente a una plaza de deportes y raras veces una bola rompe uno de los vídrios. Si la bola golpea x metros a la derecha del inicio y y metros hacia abajo del inicio, ¿en qué fila y columna se encuentra el vídrio roto? Por ejemplo, si la bola impacta en x=1.04m y y=2.17m, el mosaico a reemplazar sería el que está en la fila 5 y columna 3. Escriba un procedimiento que recibe x y y e imprime la fila y columna del vidrio que debe reemplazarse.

2.5. Tablero de memoria

Un juego de memoria ocupa la totalidad de una pantalla de 640x480 pixeles. Las cartas están en 10 filas y 10 columnas. Si el usuario hace click en el píxel (x, y) ¿En cuál fila y columna está la carta que debe voltearse? Tome en consideración que las ventanas tienen su inicio en la esquina inferior izquierda.

2.6. Imagen en la última página

Se debe imprimir un documento de R líneas en páginas tamaño oficio a las que les cabe r líneas. Se quiere imprimir una imagen de H líneas de alto en la última página (H < r), sólo si queda espacio. Diseñe una función que indique si se puede o no agregar la imagen sin tener que incurrir en una página en blanco más. Por ejemplo, una imagen de H=15 líneas cabe perfectamente en la última página, si se imprimen R=70 líneas en páginas con capacidad para r=30 línas, pero no una imagen de 21 líneas.

3. Ciclos

3.1. Estadísticas v1

Escriba un programa que lea un número C de la entrada estándar. Luego lee C números enteros de la entrada estándar. Finalmente el programa imprime: el menor de los números, el promedio, y el mayor de los valores. Por ejemplo, la entrada:

4

80
95
40
80

produciría:

Min: 40
Average: 73.75
Max: 95
Listado 15. Calcula tres estadísticos básicos. Usa ciclo por contador (for) dado que se conoce la cantidad de iteraciones de antemano
public void run() {
  final int valueCount = this.input.nextInt();

  double min = Double.POSITIVE_INFINITY;
  double max = Double.NEGATIVE_INFINITY;
  double sum = 0.0;

  for (int counter = 0; counter < valueCount; counter = counter + 1) {
    final double value = this.input.nextDouble();
    if (value < min) {
      min = value;
    }
    if (value > max) {
      max = value;
    }
    sum = sum + value;
  }

  System.out.printf("Min: %f%n", min);
  final double average = sum / valueCount;
  System.out.printf("Average: %f%n", average);
  System.out.printf("Max: %f%n", max);
}

Valide la entrada del programa anterior, de tal forma que el número C sea siempre positivo. Si es cero, no imprima ninguna estadística. Si es negativo o un texto, imprima un mensaje de error en el error estándar.

3.2. Estadísticas v2

Escriba un programa que lea todos los números reales de la entrada estándar hasta encontrar el fin-de-archivo, e imprima: la cantidad de ellos, el menor de ellos, el promedio, y el mayor de los valores. Por ejemplo, la entrada:

80
95
40
80

produciría:

Count: 4
Min: 40
Average: 73.75
Max: 95
Listado 16. Calcula cuatro estadísticos básicos. Usa ciclo por condición (while) dado que no se conoce la cantidad de iteraciones de antemano
public void run() {
  int valueCount = 0;
  double min = Double.POSITIVE_INFINITY;
  double max = Double.NEGATIVE_INFINITY;
  double sum = 0.0;

  while (this.input.hasNextDouble()) {
    final double value = this.input.nextDouble();
    if (value < min) {
      min = value;
    }
    if (value > max) {
      max = value;
    }
    sum = sum + value;
    valueCount = valueCount + 1;
  }

  System.out.printf("Count: %d%n", valueCount);
  System.out.printf("Min: %f%n", min);
  final double average = sum / valueCount;
  System.out.printf("Average: %f%n", average);
  System.out.printf("Max: %f%n", max);
}

3.3. Secuencia programada

Haga un programa que lea una secuencia de números enteros de la entrada estándar. Si el número es negativo, por ejemplo, -5, el programa "se brinca" los siguientes 5 números en la entrada estándar. Si el número es positivo, el programa lo suma a un resultado. Si el número es 0, el programa termina su ejecución e imprime la suma resultado. Por ejemplo, la siguiente entrada produce 16:

7 3 -4 1 0 -2 -8 -2 3 4 6 -1 0 0 4

Si los números se acaban antes de encontrar un cero, imprima invalid program en lugar de la suma resultado.

3.4. El arbolito de navidad

  1. Construya un programa que imprima un árbol de navidad. El programa lee de la entrada la cantidad de ramas que tiene el árbol y el carácter con que se debe imprimir el árbol. Por ejemplo, 4 * produce

       *
      ***
     *****
    *******
       *

¿Por qué esta implementación no produce el resultado correcto? Corríjala para que imprima el árbol de navidad como se espera.

3.5. Intercambio for/while

Cambie los siguientes ciclos de for a while y viceversa.

Listado 17. Ciclo 1
while (true) {
  int x = this.input.nextInt();
  if (x <= 0) {
    break;
  }
}

Solución:

// for (; true;) {
for (;;) {
  int x = this.input.nextInt();
  if (x <= 0) {
    break;
  }
}
Listado 18. Ciclo 2
int i = 1, n = this.input.nextInt(), p = 1;
while (i <= n) {
  p *= this.input.nextInt();
  ++i;
}

Solución:

for (int i = 1, n = this.input.nextInt(), p = 1; i <= n; ++i) {
  p *= this.input.nextInt();
}
Listado 19. Ciclo 3
double total = 0.0;
while (this.input.hasNextDouble()) {
  total += this.input.nextInt();
}
System.out.println(total);

Solución:

double total = 0.0;
for (; this.input.hasNextDouble(); total += this.input.nextInt()) {
  // empty loop
}
System.out.println(total);


double total = 0.0;
for (; this.input.hasNextDouble(); ) {
  total += this.input.nextInt();
}
System.out.println(total);
Listado 20. Ciclo 4
double diffSum = 0.0;
try {
  for (int c = 0; ; c++) {
    final double a = this.input.nextDouble();
    final double b = this.input.nextDouble();
    diffSum += Math.abs(a - b);
  }
  System.out.println(diffSum);
} catch (final java.util.NoSuchElementException error) {
  System.out.println(diffSum);
} catch (final java.util.InputMismatchException error) {
  System.err.println("invalid data");
}

Solución:

4. Subrutinas

5. Arreglos y matrices

5.1. Arreglos literales

Escriba otra versión de la función al problema "Días después" (Section 2.2) que use arreglos para retornar un texto con el nombre del día.

Listado 21. Quinta función que mapea el número del día con un arreglo
public String nameDaysAfter(int x) {
  final String[] days = { "domingo", "lunes", "martes", "miercoles", "jueves", "viernes", "sabado" };
  if (daysAfter(x) >= 0 && daysAfter(x) < days.length) {
    return days[daysAfter(x)];
  } else {
    throw new Exception("index out of bounds");
  }
}

5.2. Asociación con índices

Escriba un programa que cuente cuántas apariciones hay de cada letra del alfabeto inglés en un texto. Considere mayúsculas y minúsculas como si fuesen iguales. Cualquier otro carácter que no sea una letra del alfabeto en inglés, considérela como puntuación. Si una letra no aparece en el texto, no la reporte en la salida. Recuerde que los arreglos asocian números enteros no negativos conocidos como índices a datos de cualquier tipo.

Ejemplo de entrada:

Practice makes perfect.

Ejemplo de salida:

a: 2
c: 3
e: 4
f: 1
i: 1
k: 1
m: 1
p: 2
r: 2
s: 1
t: 2
*: 3

5.3. Matriz traspuesta

Haga un programa que lea una matriz de enteros grandes de la entrada estándar. En la primera línea están sus dimensiones en filas y columnas. Si la matriz es cuadrada y es la matriz identidad, imprima en la salida estándar identity matrix. De lo contrario, construya en memoria la matrix traspuesta, y luego imprímala.

Ejemplo de entrada 1:

3 4
1 0 1 3
7 1 0 1
0 0 8 5

Ejemplo de salida 1:

1 7 0
0 1 0
1 0 8
3 1 5

Ejemplo de entrada 2:

3 3
1 0 0
0 1 0
0 0 1

Ejemplo de salida 2:

identity matrix