3 PEC3
3.1 Cómo declarar un vector en lenguaje algorítmico
Cuando se declara un vector en lenguaje algorítmico no hay que declarar una variable para cada una de las posiciones de este vector.
Por ejemplo, si queremos un algoritmo que sume tres enteros contenidos dentro de un vector podemos hacer lo siguiente:
const
NUM_ENTEROS: integer = 3;
end const
algorithm sumaEnteros
var
enteros: vector[NUM_ENTERS] of integer;
suma: integer;
end var
enteros[1] := 13;
enteros[2] := 24;
enteros[3] := 2;
{ Como se puede ver, accedemos directamente a las posiciones }
{ del vector en vez de definir una variable }
{ para cada posición. }
suma := enteros[1] + enteros[2] + enteros[3];
{ Hay que recordar también que en lenguaje algorítmico, }
{ en un vector de tamaño n la primera posición del vector
{ es la 1 y la última la n; en cambio en lenguaje C }
{ la primera siempre es la 0 y la última la n-1. }
writeString("La suma de los 3 enteros del vector es: ");
writeInteger(suma);
end algorithm
3.2 Significado de los argumentos del main
La principal diferencia entre la definición main(int argc, char **argv)
y main()
es que la primera opción está preparada para recibir argumentos cuando se ejecuta el programa, mientras que la segunda no.
Por ejemplo, si tenemos el siguiente programa compilado en C y le pasamos una serie de argumentos desde la línea de comandos:
$> programa a1 a2 a3
Con el main definido como main(int argc, char **argv)
podemos acceder desde dentro del programa a todos los argumentos pasados; así tendremos:
argc = 4
argv[0] = "programa"
argv[1] = "a1"
argv[2] = "a2"
argv[3] = "a3"
El propio sistema operativo se ocupa de darle el valor al argumento int argc
(número total de argumentos, incluido el nombre del programa), con lo que únicamente tenéis que preocuparte de pasar los argumentos. Por otra parte, argv
es un array de punteros donde cada uno de ellos apunta a un argumento formado por una cadena de caracteres; así argv
contendrá en cada una de sus posiciones los argumentos pasados desde línea de comandos, y en la posición 0 el propio nombre del programa.
Si en cambio tenéis definido el programa como main ()
, simplemente no tenéis forma de acceder a los argumentos que le podáis llegar a pasar. Hay muchas veces que los datos los podéis tener ya definidos dentro del propio programa o que los vayáis a consultar a una fuente externa, con lo que no tener la capacidad de procesar argumentos no supone ningún impedimento a la hora de ejecutar vuestro programa.
3.3 Asignar valores a un vector
La lectura y asignación de valores a un vector se realiza de la siguiente forma en lenguaje algorítmico:
const
MAX_TEMP: integer = 2;
end const
algorithm lecturaTemperaturas
var
temperaturas: vector[MAX_TEMP] of float;
end var
writeString("Introduce la lectura 1 : ");
temperaturas[1] := readReal();
writeString("Introduce la lectura 2 : ");
temperaturas[2] := readReal();
writeString("Los valores introducidos han sido : ");
writeString("> Valor de la posición ");
writeInteger(1);
writeString(" : ");
writeReal(temperaturas[1]);
writeString("> Valor de la posición ");
writeInteger(2);
writeString(" : ");
writeReal(temperaturas[2]);
end algorithm
Y en lenguaje C:
/* Ejemplo ES0301 */
#include <stdio.h>
#define MAX_TEMP 2
int main(int argc, char **argv) {
float temperaturas[MAX_TEMP];
printf("Introduce la lectura 1 : ");
scanf("%f", &temperaturas[0]);
printf("Introduce la lectura 2 : ");
scanf("%f", &temperaturas[1]);
printf("> Valor de la posición %d : %.1f \n", 0, temperaturas[0]);
printf("> Valor de la posición %d : %.1f \n", 1, temperaturas[1]);
return 0;
}
Hay que remarcar una diferencia importante:
- En lenguaje algorítmico las posiciones del vector van desde la 1 hasta la N.
- En lenguaje C, van desde de la 0 hasta la N-1.
En ambos casos N hace referencia al número total de elementos del vector.
3.4 Stack smashing detected
El error stack smashing detected (code dumped)
se produce cuando se intenta acceder u operar con una posición de un vector que no lo hemos definido previamente. Se puede dar por diferentes situaciones que acaban generando el mismo problema.
- Caso 1: se define un vector de N-posiciones, pero en vez de comenzar por la posición 0 lo hacemos por la 1. Esto es incorrecto: recordemos que en C la posición inicial de un vector siempre es la 0, y la final siempre es N-1. Por ejemplo, para un vector de 3 posiciones tendremos:
int vector1[3];
vector1[1] = 13; /* Posición del vector1 válida */
vector1[2] = 24; /* Posición del vector1 válida */
vector1[3] = 48; /* ¡Posición del vector1 no válida! */
/* En cambio la posición 0 del vector,
* que tenemos disponible, ¡no la hemos utilizado!
*/
- Caso 2: se define un vector con menos posiciones de las que necesitamos. Por ejemplo, si tenemos:
int vector2[2];
Significa que las posiciones reservadas en memoria para este vector son:
vector2[0] = 10; /* Posición del vector2 válida */
vector2[1] = 13; /* Posición del vector2 válida */
vector2[2] = 24; /* ¡Posición del vector2 no válida! */
Por lo tanto, cualquier operación con vector2[2]
nos generará el error indicado. Si queremos que el vector contenga 3 elementos solo hay que definir correctamente su tamaño:
int vector2[3];
3.5 Concatenación en lenguaje algorítmico
A diferencia del lenguaje C, en notación algorítmica no está contemplado el uso de especificadores que permitan hacer concatenaciones entre cadenas de caracteres, enteros, decimales, etc.
Por lo tanto en lenguaje algorítmico hay que romper los strings con fragmentos más pequeños y que hagan referencia únicamente a un tipo de datos. Por ejemplo, si se quiere mostrar por pantalla el mensaje “El empleado que más cobra es Marta, y su nómina es 4675.30 €.”, Lo haremos de la siguiente forma:
writeString("El empleado que más cobra es ");
writeString(nombreEmpleado);
writeString(", y su nómina es ");
writeReal(nomina);
writeString(" €.");
En lenguaje C equivaldría a:
printf("El empleado que más cobra es %s, y su nómina es %.2f €.",
nombreEmpleado, nomina);
3.6 Importancia de los tipos utilizados en lenguaje C
El resultado de las operaciones en lenguaje C depende del tipo de variable definido y de los tipos de valores utilizados. A continuación se exponen tres casos que, según el tipo que se haya definido en las variables utilizadas, dará un resultado u otro:
Caso 1:
/* Ejemplo ES0302 */
#include <stdio.h>
int main(int argc, char **argv) {
int numero1;
int numero2;
int numero3;
int promedio;
printf("Teclea el primer número: ");
scanf("%d", &numero1);
printf("Teclea el segundo número: ");
scanf("%d", &numero2);
printf("Teclea el tercer número: ");
scanf("%d", &numero3);
promedio = (numero1 + numero2 + numero3) / 3;
printf("El promedio es %d", promedio);
return 0;
}
En este caso si damos los valores number1 = 1
, number2 = 3
, number3 = 4
, el resultado es average = 2
. El resultado de la división será un entero, ya que tanto numerador como denominador están formados por enteros. El resultado entero se guarda en una variable entera, y por pantalla obtendremos: 2
.
Caso 2:
/* Ejemplo ES0303 */
#include <stdio.h>
int main(int argc, char **argv) {
int numero1;
int numero2;
int numero3;
float promedio;
printf("Teclea el primer número: ");
scanf("%d", &numero1);
printf("Teclea el segundo número: ");
scanf("%d", &numero2);
printf("Teclea el tercer número: ");
scanf("%d", &numero3);
promedio = (numero1 + numero2 + numero3) / 3;
printf("El promedio es %f", promedio);
return 0;
}
Al igual que en el caso anterior, el numerador y el denominador de la división están formados por enteros, con lo que el resultado será un entero. En este caso el resultado entero lo guardamos en una variable de tipo float
, con lo que C mostrará el resultado con decimales: 2.000000
Caso 3:
/* Ejemplo ES0304 */
#include <stdio.h>
int main(int argc, char **argv) {
int numero1;
int numero2;
int numero3;
float promedio;
printf("Teclea el primer número: ");
scanf("%d", &numero1);
printf("Teclea el segundo número: ");
scanf("%d", &numero2);
printf("Teclea el tercer número: ");
scanf("%d", &numero3);
promedio = (numero1 + numero2 + numero3) / 3.0;
printf("El promedio es %f", promedio);
return 0;
}
En este caso el resultado de la división será un decimal, ya que el denominador contiene un decimal (en este caso 3.0
). El resultado con decimales se guarda en una variable de tipo float
, y por pantalla se mostrará: 2.666667
.
3.7 Ejemplo: notaFinal
El siguiente ejemplo calcula la nota final de una asignatura en función de una serie de condicionales y de operaciones:
/* Ejemplo ES0305 */
#include <stdio.h>
#include <string.h>
/* Queremos un programa que calcule la nota final
* de una asignatura. La nota final se calcula a partir
* de la EC (Evaluación continua) y la nota de la Práctica:
*
* Nota final = 30% AC + 70% Práctica
*
* Una vez introducidas todas las notas, si se
* detecta alguna que sea incorrecta (fuera del
* rango [0.0 a 10.0]), se mostrará un mensaje
* informativo por pantalla y no se realizará ninguna
* otra operación.
*
* La EC está formada por 3 PEC: PEC1, PEC2, PAC3.
* La nota de la EC se calcula mediante la
* media de las 3 PEC.
*
* Si la nota de la EC es inferior a 4, no es necesario
* realizar ningún cálculo: la asignatura queda suspendida.
*
* Si la nota de la EC es superior o igual a 4, se
* calcula la nota final con la nota de
* la Práctica.
*
* La nota final se mostrará en formato "grade letters",
* según la siguiente relación:
*
* MH: 10
* A: de 9.0 a 9.9
* B: de 7.0 a 8.9
* C+: de 5.0 a 6.9
* C-: de 3.0 a 4.9
* D: de 0.0 a 2.9
*
* Los puntos que trata este ejemplo:
* - definición de vectores
* - utilización del condicional if-else
* - condicionales if-else anidados
* - asignación de un string a una variable con strcpy
* - reserva espacio para el finalizador '\0'
*/
#define PEC1 0
#define PEC2 1
#define PEC3 2
#define PRA 3
#define MAX_ACTIVITADES 4
#define MAX_CHARS 2+1 /* el +1 corresponde al finalizador '\0' */
#define PESO_EC 0.3 /* EC 30% peso en la nota final */
#define PESO_PRA 0.7 /* PRA 70% peso en la nota final */
int main(int argc, char **argv) {
/* Vector que contiene las notas de
* todas las actividades del curso
*/
float notas[MAX_ACTIVITADES];
/* Nota de evaluación continua: equivale
* a la media de las 3 PEC
*/
float notaEC;
float notaFinalNumerica;
/* String que contiene la nota final
* de la asignatura (MH, A, B...)
*/
char notaFinal[MAX_CHARS];
/* Se pide por teclado las notas
* de las 3 PEC y de la PRA
*/
printf("Nota PEC1: ");
/* La asignación del valor se puede
* hacer directamente sobre una posición
* del vector
*/
scanf("%f", ¬as[PEC1]);
/* Ídem para el resto de actividades */
printf("Nota PEC2: ");
scanf("%f", ¬as[PEC2]);
printf("Nota PEC3: ");
scanf("%f", ¬as[PEC3]);
printf("Nota PRA: ");
scanf("%f", ¬as[PRA]);
/* En primer lugar, comprobamos que
* todas las notas del vector estén
* dentro del rango [0.0 .. 10.0]
*/
if (notas[PEC1] > 10.0 || notas[PEC2] > 10.0 ||
notas[PEC3] > 10.0 || notas[PRA] > 10.0 ||
notas[PEC1] < 0.0 || notas[PEC2] < 0.0 ||
notas[PEC3] < 0.0 || notas[PRA] < 0.0) {
printf("\n>> Error detectado en una o más notas:");
printf("\n>> Se detiene el cálculo de la nota final.\n");
} else {
/* En este punto sabemos que las notas
* están dentro del rango [0.0 .. 10.0]
*/
/* Comprobamos ahora que la media de las 3 PEC
* no es inferior a 4
*/
notaEC = (notas[PEC1] + notas[PEC2] + notas[PEC3]) / 3.0;
if (notaEC < 4) {
/* Para dar mejor visibilidad, mostramos únicamente
* el primer decimal de las notas numéricas
*/
printf("\n>> Nota mínima de EC insuficiente: %.1f", notaEC);
printf("\n>> Se detiene el cálculo de la nota final.\n");
} else {
/* En este punto todas las notas son correctas,
* por lo que se realiza el cálculo de la
* nota final
*/
notaFinalNumerica = notaEC * PESO_EC + notas[PRA] * PESO_PRA;
/* Ahora falta saber qué "grade letter" corresponde
* a la notaFinalNumerica calculada; lo solucionamos
* con nuevos if-else anidados
*/
if (notaFinalNumerica <= 2.9) {
/* Para asignar un string a una variable
* de tipo string usamos el comando
* strcpy en vez de '='
*/
strcpy(notaFinal, "D");
} else {
if (notaFinalNumerica <= 4.9) {
strcpy(notaFinal, "C-");
} else {
if (notaFinalNumerica <= 6.9) {
strcpy(notaFinal, "C+");
} else {
if (notaFinalNumerica <= 8.9) {
strcpy(notaFinal, "B");
} else {
if (notaFinalNumerica <= 9.9) {
strcpy(notaFinal, "A");
} else {
strcpy(notaFinal, "MH");
}
}
}
}
}
/* Para finalizar, mostramos todos los resultados
* calculados por pantalla
*/
printf("\n>> Nota EC: %.1f", notaEC);
printf("\n>> Nota PRA: %.1f", notas[PRA]);
printf("\n>> Nota final: %s (%.1f)\n", notaFinal, notaFinalNumerica);
}
}
return 0;
}
3.8 Ejemplo: alquilerFurgoneta
Imaginemos que tenemos una empresa de alquiler de furgonetas y queremos ofrecer a nuestros posibles clientes un comparador de precios. Este comparador tendrá en cuenta el número de kilómetros estimado que hará el cliente con nuestra furgoneta y el número de días que la tendrá en alquiler.
Para calcular el importe del alquiler utilizaremos dos valores: por un lado el consumo por kilómetro de la furgoneta, y por otro lado el precio de alquiler por día. Estos valores variarán en función de la furgoneta que quieran alquilar, y cada uno de ellos lo guardaremos en un vector diferente. La posición (índice) del vector nos indicará qué furgoneta estamos tratando.
El programa pedirá desde el canal estándar de entrada el número de días que se quiere alquilar y los kilómetros estimados. A continuación calculará el coste total de alquiler para cada una de las furgonetas y guardará el resultado en otro vector (el índice continuará siendo el identificador de la furgoneta).
Debido a la gran cantidad de demanda de furgonetas, sólo nos quedan tres para alquilar. El programa debe resolverse únicamente con vectores y bloques condicionales if-else
(no se pueden utilizar bucles). El programa debe devolver como resultado cuál es la furgoneta más económica de las tres en función de los datos entradas, desglosando todos los elementos que se han tenido en cuenta para realizar el cálculo.
En primer lugar, nos plantearemos mentalmente qué debe hacer nuestro algoritmo:
- Definir las constantes.
- Definir las variables.
- Leer los valores requeridos desde el canal de entrada estándar.
- Calcular el coste total y guardarlo en el vector de resultados.
- Mirar cuál de las furgonetas es la más económica.
- Mostrar por pantalla el desglose detallado de la más económica.
Una posible forma de codificar el algoritmo puede ser la siguiente:
const
NUM_FURGONETAS: integer = 3;
PRECIO_LITRO_COMBUSTIBLE: real = 1.912;
end const
algorithm alquilerFurgoneta
var
consumosPorKm: vector[NUM_FURGONETAS] of real;
preciosAlquilerPorDia: vector[NUM_FURGONETAS] of real;
preciosFinales: vector[NUM_FURGONETAS] of real;
{ Valores de entrada por teclado }
numDiasAlquiler: integer;
previsionKm: integer;
{ Utilizaremos la siguiente variable para saber cuál de ellas
es la más económica de todas, y así poder mostrar el resultado
final. }
furgonetaMasEconomica: integer;
end var
{ Inicializamos los vectores con los datos de cada furgoneta.
Los siguientes valores forman parte de nuestra aplicación
de alquiler, no se piden por el canal de entrada. }
consumosPorKm[1] := 8.25/100.0;
consumosPorKm[2] := 12.0/100.0;
consumosPorKm[3] := 9.5/100.0;
preciosAlquilerPorDia[1] := 80.5;
preciosAlquilerPorDia[2] := 69.9;
preciosAlquilerPorDia[3] := 72.8;
{ Lectura de valores desde el canal de entrada. }
writeString("¿Cuántos días quieres alquilar la furgoneta? ");
numDiasAlquiler := readInteger();
writeString("¿Cuántos kilometros harás?");
previsionKm := readInteger();
{ Cálculo de costes totales por furgoneta. }
preciosFinales[1] := preciosAlquilerPorDia[1] * integerToReal(numDiasAlquiler) +
consumosPorKm[1] * integerToReal(previsionKm) * PRECIO_LITRO_COMBUSTIBLE;
preciosFinales[2] := preciosAlquilerPorDia[2] * integerToReal(numDiasAlquiler) +
consumosPorKm[2] * integerToReal(previsionKm) * PRECIO_LITRO_COMBUSTIBLE;
preciosFinales[3] := preciosAlquilerPorDia[3] * integerToReal(numDiasAlquiler) +
consumosPorKm[3] * integerToReal(previsionKm) * PRECIO_LITRO_COMBUSTIBLE;
{ Miramos cuál de las tres es la más económica, comparando los correspondientes
costes totales de las furgonetas. Fijáos que se utiliza una estructura de
condicionales anidados (uno dentro del otro). Guardamos el identificador
de la opción más económica dentro de la variable furgonetaMasEconomica. }
if preciosFinales[1] ≤ preciosFinales[2] and
preciosFinales[1] ≤ preciosFinales[3] then
furgonetaMasEconomica := 1;
else
if preciosFinales[2] ≤ preciosFinales[1] and
preciosFinales[2] ≤ preciosFinales[3] then
furgonetaMasEconomica := 2;
else
furgonetaMasEconomica := 3;
end if
end if
{ Se muestra el detalle de valores de la furgoneta más económica
de todas, así como su importe final. }
writeString("La furgoneta más económica es la número:");
writeInteger(furgonetaMasEconomica);
writeString(">> Consumo litros por kilómetro:");
writeReal(consumosPorKm[furgonetaMasEconomica]);
writeString(">> Precio por litro de combustible:");
writeReal(PRECIO_LITRO_COMBUSTIBLE);
writeString("€");
writeString(">> Previsión kilómetros: ");
writeInteger(previsionKm);
writeString(">> Precio de alquiler diario:");
writeReal(preciosAlquilerPorDia[furgonetaMasEconomica]);
writeString("€");
writeString(">> Días de alquiler:");
writeInteger(numDiasAlquiler);
writeString(">> Importe total: ");
writeReal(preciosFinales[furgonetaMasEconomica]);
writeString(" €");
end algorithm
Una vez tenemos el algoritmo definido, sólo queda que traducirlo a lenguaje C:
/* Ejemplo ES0306 */
#include <stdio.h>
#define NUM_FURGONETAS 3
#define PRECIO_LITRO_COMBUSTIBLE 1.912
int main(int argc, char **argv) {
float consumosPorKm[NUM_FURGONETAS];
float preciosAlquilerPorDia[NUM_FURGONETAS];
/* El coste total de cada furgoneta se guardará en el vector
* preciosFinales.
*/
float preciosFinales[NUM_FURGONETAS];
/* Valores de entrada desde teclado */
int numDiasAlquiler;
int previsionKm;
/* Utilizaremos la siguiente variable para saber cuál de ellas
* es la más económica de todas, y así poder mostrar el resultado
* final.
*/
int furgonetaMasEconomica;
/* Inicializamos los vectores con los datos de cada furgoneta.
* Los siguientes valores forman parte de nuestra aplicación
* de alquiler, no se piden por teclado.
*/
consumosPorKm[0] = 8.25/100.0;
consumosPorKm[1] = 12.0/100.0;
consumosPorKm[2] = 9.5/100.0;
preciosAlquilerPorDia[0] = 80.5;
preciosAlquilerPorDia[1] = 69.9;
preciosAlquilerPorDia[2] = 72.8;
/* Lectura de valores desde teclado. */
printf("Quants dies vols llogar la furgoneta? ");
scanf("%d", &numDiasAlquiler);
printf("Quants kilometres faràs? ");
scanf("%d", &previsionKm);
/* Cálculo de costes totales por furgoneta */
preciosFinales[0] = preciosAlquilerPorDia[0] * (float)numDiasAlquiler +
consumosPorKm[0] * (float)previsionKm * PRECIO_LITRO_COMBUSTIBLE;
preciosFinales[1] = preciosAlquilerPorDia[1] * (float)numDiasAlquiler +
consumosPorKm[1] * (float)previsionKm * PRECIO_LITRO_COMBUSTIBLE;
preciosFinales[2] = preciosAlquilerPorDia[2] * (float)numDiasAlquiler +
consumosPorKm[2] * (float)previsionKm * PRECIO_LITRO_COMBUSTIBLE;
/* Miremos cuál de las tres es la más económica,
* comparando los correspondientes costes totales de las furgonetas.
* Fijáos que se utiliza una estructura de condicionales anidados
* (uno dentro del otro). Guardamos el identificador de la opción
* más económica dentro de la variable furgonetaMasEconomica.
*/
if (preciosFinales[0] <= preciosFinales[1] &&
preciosFinales[0] <= preciosFinales[2]) {
furgonetaMasEconomica = 0;
} else {
if (preciosFinales[1] <= preciosFinales[0] &&
preciosFinales[1] <= preciosFinales[2]) {
furgonetaMasEconomica = 1;
} else {
furgonetaMasEconomica = 2;
}
}
/* Se muestra el detalle de valores de la furgoneta más económica
* de todas, así como su importe final.
*/
printf("\nLa furgoneta más económica es la número %d: \n",
furgonetaMasEconomica);
printf(">> Consumo litros por kilómetro: %.2f \n",
consumosPorKm[furgonetaMasEconomica]);
printf(">> Precio por litro de combustible: %.2f € \n",
PRECIO_LITRO_COMBUSTIBLE);
printf(">> Previsión kilometros: %d \n",
previsionKm);
printf(">> Precio de alquiler diario: %.2f €\n",
preciosAlquilerPorDia[furgonetaMasEconomica]);
printf(">> Días de alquiler: %d \n",
numDiasAlquiler);
printf(">> Importe total: %.2f € \n",
preciosFinales[furgonetaMasEconomica]);
return 0;
}
Cuando miramos cuál de las furgonetas es la más económica, utilizamos condicionales anidados (uno dentro del otro). Esto nos permite hacer más óptimo nuestro programa: en caso de que detectemos que se cumple la primera de las condiciones (la furgoneta más económica es la 0), ya no hacemos otra comprobación de las que quedarían pendientes (la más económica es la 1 ? la más económica es la 2?). Es muy importante tener siempre presente este planteamiento, para que sus programas no realicen más comprobaciones de las necesarias.
Además, hemos utilizado la variable furgonetaMasEconomica
para saber cuál de ellas es la más económica de las 3. El valor que contiene esta variable es realmente un índice, para que así cuando se muestren los datos de la más económica por pantalla, simplemente lo utilizaremos en los vectores e iremos recuperando los valores de la posición indicada por el índice. Así nos ahorramos el tener que guardar los valores de todos los vectores en variables auxiliares de tipo consumoPorKmFurgonetaEconomica
,precioAlquilerPorDiaFurgonetaEconomica
, costeTotalFurgonetaEconomica
. Utilizando una única variable, furgonetaMasEconomica
, obviamos la creación de múltiples variables innecesarias.
3.9 Ejemplo: aprobarAsignatura
A continuación se plantea un ejemplo que nos permitirá saber cómo podemos superar una asignatura. La aproximación inicial, poco óptima, evalúa todas las condiciones posibles utilizando condicionales anidados:
/* Ejemplo ES0307 */
#include <stdio.h>
#include <stdbool.h>
/* Queremos un programa que nos indique si hemos superado la asignatura
* a partir de los resultados obtenidos en las 4 áreas de evaluación:
* la evaluación continua (EC), las prácticas (PR), la prueba de
* síntesis (PS) y el examen final (EX).
* A partir del plan docente, vemos que la asignatura se puede superar
* en los siguientes casos
* 1. Si se supera el AC, la PR y la PS.
* 2. Si se supera la AC, la PR y el EX.
* 3. Si se supera la PR y el EX.
* Una posible solución (¡¡NADA ÓPTIMA Y MUY MEJORABLE!!) sería...
* /
int main(int argc, char **argv) {
bool hasEC;
bool hasPR;
bool hasPS;
bool hasEX;
bool asignSuperada;
int intToBool;
printf("¿Has superado la evaluación continua? (0-false, 1-true) :");
scanf("%d", &intToBool);
hasEC = intToBool;
printf("¿Has superado las prácticas? (0-false, 1-true) :");
scanf("%d", &intToBool);
hasPR = intToBool;
printf("¿Has superado la prueba de síntesis? (0-false, 1-true) :");
scanf("%d", &intToBool);
hasPS = intToBool;
printf("¿Has superado el examen? (0-false, 1-true) :");
scanf("%d", &intToBool);
hasEX = intToBool;
if (hasEC) {
if (hasPR) {
if (hasPS) {
if (hasEX) {
asignSuperada = true;
} else {
asignSuperada = true;
}
} else {
if (hasEX) {
asignSuperada = true;
} else {
asignSuperada = false;
}
}
} else {
if (hasPS) {
if (hasEX) {
asignSuperada = false;
} else {
asignSuperada = false;
}
} else {
if (hasEX) {
asignSuperada = false;
} else {
asignSuperada = false;
}
}
}
} else {
if (hasPR) {
if (hasPS) {
if (hasEX) {
asignSuperada = true;
} else {
asignSuperada = false;
}
} else {
if (hasEX) {
asignSuperada = true;
} else {
asignSuperada = false;
}
}
} else {
if (hasPS) {
if (hasEX) {
asignSuperada = false;
} else {
asignSuperada = false;
}
} else {
if (hasEX) {
asignSuperada = false;
} else {
asignSuperada = false;
}
}
}
}
if (asignSuperada) {
printf(">> Asignatura superada. ¡Enhorabuena! \n");
} else {
printf(">> Asignatura no superada. \n");
}
return 0;
}
Esta gran estructura de condicionales anidados se puede simplificar. Si mostramos todos los posibles valores que pueden tomar las variables booleanas en una tabla, tenemos que:
hasEC | hasPR | hasPS | hasEX | asignSuperada |
---|---|---|---|---|
false | false | false | false | false |
false | false | false | true | false |
false | false | true | false | false |
false | false | true | true | false |
false | true | false | false | false |
false | true | false | true | true |
false | true | true | false | false |
false | true | true | true | true |
true | false | false | false | false |
true | false | false | true | false |
true | false | true | false | false |
true | false | true | true | false |
true | true | false | false | false |
true | true | false | true | true |
true | true | true | false | true |
true | true | true | true | true |
Como se puede ver, únicamente se aprobará la asignatura en dos casos:
- Si se ha superado la práctica (hasPR), la evaluación continua (hasEC) y la prueba de síntesis (hasPS).
- Si se ha superado la práctica (hasPR) y el examen (hasEX).
Por lo tanto, podemos sustituir todos los condicionales anidados por una expresión única:
asignSuperada = (hasPR && hasEC && hasPS) || (hasPR && hasEX)
Que también equivale a:
asignSuperada = hasPR && ((hasEC && hasPS) || hasEX)
Así, una solución mucho más óptima y correcta es la siguiente:
/* Ejemplo ES0308 */
#include <stdio.h>
#include <stdbool.h>
/* Codificación alternativa del mismo ejercicio: más eficiente y óptima. */
int main(int argc, char **argv) {
bool hasEC;
bool hasPR;
bool hasPS;
bool hasEX;
bool asignSuperada;
int aux;
printf("¿Has superado la evaluación continua? (0-false, 1-true) :");
scanf("%d", &aux);
hasEC = aux;
printf("¿Has superado las prácticas? (0-false, 1-true) :");
scanf("%d", &aux);
hasPR = aux;
printf("¿Has superado la prueba de síntesis? (0-false, 1-true) :");
scanf("%d", &aux);
hasPS = aux;
printf("¿Has superado el examen? (0-false, 1-true) :");
scanf("%d", &aux);
hasEX = aux;
/* Expresión equivalente */
asignSuperada = hasPR && ((hasEC && hasPS) || hasEX);
if (asignSuperada) {
printf(">> Asignatura superada. !Enhorabuena! \n");
} else {
printf(">> Asignatura no superada. \n");
}
return 0;
}
3.10 Errores más frecuentes
3.10.1 Strings: declaración como vectores de caracteres en lenguaje algorítmico
Pseudocódigo incorrecto:
var
name: vector[MAX_CHAR] of char;
end var
En el algoritmo anterior, se intenta emular el lenguaje C a la hora de declarar una variable de tipo string
, declarándola como un vector de caracteres. Esto es incorrecto y absolutamente innecesario, porque en lenguaje algorítmico sí que existe el tipo string
, y por tanto su declaración es mucho más sencilla.
Pseudocódigo correcto:
var
name: string;
end var
3.10.2 Sintaxis propia de C: funciones complejas
Pseudocódigo incorrecto:
function hotelCmp(hotel1: tHotel, hotel2: tHotel): boolean;
if strcmp(hotel1.brand, hotel2.brand) = 0 then
{...}
end if
end function
La intención del algoritmo es comparar dos campos de tipo string
de las variables hotel1
y hotel2
. Sin embargo se hace uso de la función strcmp()
, propia de C e inexistente en lenguaje algorítmico.
En lenguaje algorítmico, la comparación de dos cadenas de caracteres es mucho más sencilla: se usa directamente =
.
Pseudocódigo correcto:
function hotelCmp(hotel1: tHotel, hotel2: tHotel): boolean;
if (hotel1.brand = hotel2.brand) then
{ ... }
end if
3.10.3 Estructura alternativa: condicionales consecutivos
Este no es un error sintáctico o semántico, sino una mala práctica de diseño.
Pseudocódigo incorrecto:
if discountHotel ≥ 0 and discountHotel ≤ 10 then
writeString("Invalid data");
end if
if discountHotel > 10 and discountHotel ≤ 20 then
writeString("Not bad");
end if
if discountHotel > 20 and discountHotel ≤ 50 then
writeString("Good!");
end if
El algoritmo anterior quiere mostrar un mensaje en pantalla en función del valor de la variable discountData
. Para este propósito nada mejor que una estructura alternativa, pero no de la forma en que está diseñada.
En este caso que se han construido tres bloques if ... end if
independientes y consecutivos. Esto significa que durante la ejecución todos los bloques se evaluarán de forma consecutiva para decidir si hay que ejecutar el código interior o no. Esto no es necesario ni deseable, ya que aumenta el tiempo de ejecución.
Siempre que podamos, hay que construir estructuras alternativas anidadas y excluyentes, de forma que solo se evalúen las condiciones imprescindibles.
Pseudocódigo correcto:
if discountHotel ≥ 0 and discountHotel ≤ 10 then
writeString("Invalid data");
else
if discountHotel > 10 and discountHotel ≤ 20 then
writeString("Not bad");
else
if discountHotel > 20 and discountHotel ≤ 50 then
writeString("Good!");
end if
end if
end if
3.10.4 Estructura alternativa: condicionales vacíos
Este no es un error sintáctico o semántico, sino una mala práctica de diseño.
Pseudocódigo incorrecto:
if discountHotel ≥ 0 then
else
writeString("Invalid data");
end if
El algoritmo anterior quiere mostrar un mensaje de error en pantalla si el valor de la variable discountHotel
es negativo, pero la estructura para hacerlo no es nada apropiada. En caso de que se cumpla la condición discountHotel ≥ 0
, la estructura alternativa no ejecuta nada. Recordemos que es posible una estructura if
sin else
, y de hecho parece que en este caso se haya añadido únicamente porque se creía lo contrario.
Pseudocódigo correcto:
if discountHotel < 0 then
writeString("Invalid data");
end if
3.10.5 Estructura alternativa: interrumpir un algoritmo (I)
Pseudocódigo incorrecto:
algorithm nameAlgorithm
if discountHotel < 0 then
writeString("Invalid data");
end algorithm;
else
writeString("Continue...");
{ ... }
end if
A menudo nos piden interrumpir la ejecución de un algoritmo en caso de que se dé alguna situación, por ejemplo, un error en la entrada de datos. A nivel de diseño algorítmico, no hay ninguna función ni sentencia pensada para efectuar esta acción de forma explícita. En el agoritmo del ejemplo, se intenta hacerlo utilizando la sentencia end algorithm
, pero esto no es correcto.
Sencillamente hay que montar la estructura alternativa de forma que cuando se produzca el error no se ejecute otra sentencia, por ejemplo poniendo todo el código a ejecutar dentro del bloque else
. Hay que asegurar, eso sí, que una vez que salimos del bloque if
…else
, el algoritmo no tiene nada pendiente por ejecutar.
Un diseño mucho más elegante tendría las condiciones inversas, dejando siempre los errores y la salida del algoritmo dentro del bloque else
:
Pseudocódigo correcto:
algorithm nameAlgorithm
if discountHotel ≥ 0 then
writeString("Continue...");
{ Do something }
else
writeString("Invalid data");
{ Nothing else to do }
end if
end algorithm;
3.10.6 Estructura alternativa: interrumpir un algoritmo (II)
Pseudocódigo incorrecto:
algorithm nameAlgorithm
if discountHotel < 0 then
writeString("Invalid data");
exit();
else
writeString("Continue...");
{ ... }
end if
Hay veces en las que se quiere emular en lenguaje algorítmico algunas funciones de C que interrumpen la ejecución del código, como por ejemplo exit()
. Es incorrecto debido a que esta función no existe en lenguaje algorítmico; de hecho, en lenguaje C, aunque exista, también hay que evitarla, ya que su uso no es una buena práctica de programación.
Pseudocódigo correcto:
algorithm nameAlgorithm
if discountHotel ≥ 0 then
writeString("Continue...");
{ Do something }
else
writeString("Invalid data");
{ Nothing else to do }
end if
end algorithm;
3.10.7 Constantes y números: Valores numéricos en el código (hardcode)
Pseudocódigo incorrecto:
const
NUM_SEATS1: integer = 34;
NUM_RIDES: integer = 3;
A1: integer = 1;
A2: integer = 2;
A3: integer = 3;
end const
var
emptySeats: vector[3] of integer;
end var
{ input values }
writeString("EMPTY SEATS");
writeString(NAME_RIDE1);
writeString(" >> ");
emptySeats[1] := readInteger();
En el algoritmo anterior, se declara un vector de enteros, emptySeats
, con una longitud igual a 3 posiciones. Posteriormente, se lee un entero y se guarda en la primera posición del vector. En el enunciado del ejercicio se daban unas constantes ya declaradas, y se pedía explícitamente utilizarlas para operar con los vectores.
El motivo de la declaración de constantes no es otro que introducir la (buena) costumbre de utilizarlas y evitar al máximo los valores numéricos directos en el código (técnica conocida como hardcode). De esta manera, es mucho más fácil mantener el código posteriormente y realizar modificaciones.
En caso de que quisiéramos modificar la longitud del vector, o guardar los valores en posiciones diferentes, solo deberíamos modificar la constante al principio del código, en vez de tener que modificar todas las líneas donde aparecen estos valores numéricos.
Pseudocódigo correcto:
const
NUM_SEATS1: integer = 34;
NUM_RIDES: integer = 3;
A1: integer = 1;
A2: integer = 2;
A3: integer = 3;
end const
var
emptySeats: vector[NUM_RIDES] of integer;
end var
{input values}
writeString("EMPTY SEATS");
writeString(NAME_RIDE1);
writeString(" >> ");
emptySeats[A1] := readInteger();
3.10.8 Constantes: declaración de constantes mal ubicada
Código incorrecto:
#include <stdio.h>
int main(int argc, char **argv) {
#define MAX_LEN 15
char brand[MAX_LEN];
return 0;
}
Las constantes deben declararse al principio del bloque de código, y fuera de cualquier función (sea el main
u otra), ya que al igual que los tipos de datos, las constantes se definen de forma global para todo el programa. No es correcto abrir un bloque de declaración de constantes dentro de un bloque central de código, y mucho menos abrir varios bloques de constantes a medida que las necesitamos.
Código correcto:
#include <stdio.h>
#define MAX_LEN 15
int main(int argc, char **argv) {
char brand[MAX_LEN];
/* ... */
return 0;
}
3.10.9 Strings: comparación directa
Código incorrecto:
#include <stdio.h>
#define MAX_LEN 15
int main(int argc, char **argv) {
char name1[MAX_LEN];
char name2[MAX_LEN];
if (name1 == name2) {
/* ... */
}
return 0;
}
En C las cadenas de caracteres (así como el resto de vectores) no se pueden comparar directamente con el operador ==
. En su lugar, hay dos opciones:
- En el caso de los vectores, se pueden comparar elemento a elemento, de forma individual.
- En el caso de los strings, C dispone de funciones específicas tales como
strcmp()
.
Código correcto:
#include <stdio.h>
#define MAX_LEN 15
int main(int argc, char **argv) {
char name1[MAX_LEN];
char name2[MAX_LEN];
if (strcmp(name1, name2) == 0) {
/* ... */
}
return 0;
}