Está en la página 1de 6

Ejercicios complementarios al práctico 7 - Comentarios

Programación 1
2018

Todas las compilaciones a las que se harán referencia hablan del siguiente programa,
donde los fragmento A y B varı́an a lo largo de la nota.
program comp7 ;
const N = 4;
type
Rango = 1.. N ;
Arreglo = array [ Rango ] of Integer ;
var
i : integer ;
ordenado : boolean ;
tabla : Arreglo ;

begin
... fragmento A ...
... fragmento B ...
if ordenado then writeln ( ’ Ordenado ’) else writeln ( ’ Desordenado ’)
end .

1. Discusión de los ejercicios complementarios


Ejercicio 1. Considere el siguiente fragmento de código para cargar elementos en las celdas
del arreglo. Si no compila, corrı́jalo para que compile. Si produce errores en tiempo de
ejecución, corrı́jalo para que no se produzcan. ¿Produce el resultado esperado? ¿Utiliza la(s)
estructura(s) de control adecuada(s)? Explique.
for i := 1 to N do
read ( Arreglo [ i ]);
..........................................................................................
Compilamos el programa usando el fragmento anterior como A, y sin nada como fragmento
B. Obtenemos el siguiente error de compilación.
... Error: Variable identifier expected
El compilador nos informa que esperaba un identificador para una variable, pero ha
encontrado otra cosa; ha sido detectado un problema de sintaxis. El problema surge al usar
Arreglo que es el nombre de un tipo declarado en la sección type. El valor leı́do por read
debe almacenarse en una variable declarada en la sección var correspondiente, y no en un
tipo. Corregimos el problema cambiando Arreglo por tabla, que es la variable definida en la
sección var para manipular el arreglo. Todos los valores que manejamos dentro del programa

1
están asociados a alguna variable; el tipo de una variable indica las caracterı́sticas de sus
posibles valores, pero nunca un valor.
Podemos leer lo siguiente en la Guı́a de Usuario de Free Pascal1

Error: Variable identifier expected This happens when you pass a constant
to a routine (such as Inc var or Dec) when it expects a variable. You can only
pass variables as arguments to these functions.

Se ha usado un for para cargar todo el arreglo. De esta manera estamos seguros que
todas las celdas serán inicializadas desde la entrada estándar.
A partir de ahora el fragmento A siempre será la siguiente lı́nea:
for i := 1 to N do read ( tabla [ i ]);
Y los restantes fragmentos que aparezcan se corresponden con B.

Resumen: el programa original no compila, se corrige colocando el nombre de la va-


riable en el lugar que erróneamente ocupa el nombre del tipo, no produce errores en tiempo
de ejecución, produce el resultado esperado, y usa la estructura for adecuadamente.

Ejercicio 2. Considere el siguiente fragmento de código para determinar si el arreglo está


ordenado de menor a mayor. Si no compila, corrı́jalo para que compile. Si produce errores
en tiempo de ejecución, corrı́jalo para que no se produzcan. ¿Produce el resultado esperado?
¿Utiliza la(s) estructura(s) de control adecuada(s)? Explique.
ordenado := False ;
for i := 1 to N do
if tabla [ i ] < tabla [ i +1] then
ordenado := True ;
if ordenado then writeln ( ’ Ordenado ’) else writeln ( ’ Desordenado ’)
.......................................................................................
La compilación es exitosa. Sin embargo, al ejecutar tenemos un problema.

[Complementarios]$ ./comp7
1 2 3 4
Runtime error 201 at $00000000004002C0
$00000000004002C0 line 17 of comp7.pas
$000000000040018F

El mensaje de error tiene distintas partes; informa que es el error 201, ası́ lo podemos
buscar en el manual y saber qué pasó, y nos dice que aparece en la lı́nea 17 de nuestro
archivo2 , ası́ lo podemos encontrar rápidamente. También aparece otro mensaje, pero que
desde nuestro punto de vista no resulta informativo.
En la Guı́a del Usuario vemos el significado del error.

201 Range check error If you compiled your program with range checking on3 ,
then you can get this error in the following cases:

1. An array was accessed with an index outside its declared range.


1
www.freepascal.org/docs-html/current/user/userse63.html#x177-184000C.4
2
Este es el comportamiento que proporciona la opción -gl.
3
Este es el comportamiento que proporciona las opciones -Co -Cr.

2
2. Trying to assign a value to a variable outside its range (for instance an
enumerated type).

La lı́nea 17 dice
if tabla [ i ] < tabla [ i +1] then
Por lo tanto, debemos investigar si i e i+1 están siempre en el rango de tabla. El menor
valor que toma i es 1, ası́ que no habrá problemas si N es mayor que uno; y el mayor valor
que toma i es N. Aquı́ si tenemos un problema ya que al final de la iteración preguntaremos
siempre por tabla[N+1], quedando fuera del rango.
Una forma equivocada de resolver este problema es fortaleciendo el condicional de la
siguiente forma
ordenado := False ;
for i := 1 to N do
if ( i + 1 <= N ) and ( tabla [ i ] < tabla [ i +1]) then
ordenado := True ;
Este intento de solución no da error de rango, pero es conceptualmente incorrecta, ya que para
evitar una iteración particular agrega una nueva condición en cada una de las N iteraciones
del for. El intento adecuado pasa por cambiar los lı́mites del bucle.
ordenado := False ;
for i := 1 to N -1 do
if tabla [ i ] < tabla [ i +1] then
ordenado := True ;
Observemos las siguientes ejecuciones del programa.

[Complementarios]$ ./comp7
1 2 3 4
Ordenado
[Complementarios]$ ./comp7
1 2 4 3
Ordenado
[Complementarios]$ ./comp7
999 888 999 888
Ordenado

Resulta que no estamos investigando si todas las parejas adyacentes cumplen la relación
de menor; estamos investigando si alguna pareja la cumple. La única posibilidad para que el
programa escriba Desordenado es que el arreglo sea decreciente.
La observación anterior nos proporciona una idea para corregir el código; investigaremos
si alguna pareja no cumple la relación de orden. La siguiente versión resuelve el problema.
ordenado := True ;
for i := 1 to N -1 do
if tabla [ i ] >= tabla [ i +1] then
ordenado := False ;
Otra posible solución con for serı́a usar ordenado como acumulador.
ordenado := True ;
for i := 1 to N -1 do
ordenado := ordenado and ( tabla [ i ] < tabla [ i +1])

3
Observemos que si ordenado toma el valor False no podrá volver a tomar el valor True.
El problema de decidir si el arreglo es creciente se ha reformulado como el problema de
decidir si alguna pareja adyacente de sus elementos está desordenada; es un problema de
búsqueda en un arreglo. Ya hemos visto que en estos casos la estructura repetitiva a usar
debe permitirnos terminar con el bucle apenas sea posible. Por lo tanto, aunque el código
determine si el arreglo está ordenado o no, el uso del for es un error conceptual importante.

Resumen: el programa original compila, produce errores en tiempo de ejecución (si se


compila con las opciones adecuadas), no produce el resultado esperado, y usa la estructura
for inadecuadamente.

Ejercicio 3. Considere el siguiente fragmento de código para determinar si el arreglo está


ordenado de menor a mayor. Si no compila, corrı́jalo para que compile. Si produce errores
en tiempo de ejecución, corrı́jalo para que no se produzcan. ¿Produce el resultado esperado?
¿Utiliza la(s) estructura(s) de control adecuada(s)? Explique.
i := 1;
while ( i <= N ) and ( tabla [ i ] < tabla [ i +1]) do
i := i + 1;
ordenado := i > N ;
.......................................................................................
Aunque la compilación es exitosa, la ejecución presenta el mismo error 201 analizado en
la parte anterior. En este caso la lı́nea señalada es
while ( i <= N ) and ( tabla [ i ] < tabla [ i +1]) do
Las únicas posibilidades para estar fuera de rango son, como antes, los dos accesos al arreglo.
Al igual que antes, la solución pasa por excluı́r del bucle la última iteración, y actualizar la
asignación de ordenado.
i := 1;
while ( i <= N - 1) and ( tabla [ i ] < tabla [ i +1]) do
i := i + 1;
ordenado := i > N - 1;

Resumen: el programa original compila, produce errores en tiempo de ejecución (si


se compila con las opciones adecuadas), produce el resultado esperado, y usa la estructura
while adecuadamente.

Revisando las transparencias. En las transparencias sobre arreglos y subrangos se


plantea un esquema general de búsquedas en arreglos de la forma:
i := PRIMERO ;
while ( i <= ULTIMO ) and not cumple_condicion ( a [ i ]) do
i := siguiente ( i );
donde PRIMERO es el primer ı́ndice y ULTIMO el último ı́ndice usados en la iteración. En
nuestro caso concreto, esos valores son 1 y N-1.
Consideremos ahora el problema de decidir si alguna pareja adyacente de los elementos del
arreglo está desordenada. La condición que buscamos es encontrar dos elementos adyacentes
desordenados. Ası́ que el esquema general propuesto nos sugiere la siguiente solución.

4
i := 1;
while ( i <= N - 1) and not ( tabla [ i ] >= tabla [ i +1]) do
i := i + 1;
Es fácil constatar que este código y la última versión considerada en el último problema son
básicamente los mismos.

2. Compilación
En esta sección miraremos con mayor detalle algunos temas de compilación y ejecución.

Salida completa de una compilación fallida. Al compilar el primer problema se obtiene


lo siguiente.

[Complementarios]$ fpc -Co -Cr -Mtp -gl comp7.pas


Free Pascal Compiler version 3.0.0 [2015/11/20] for x86_64
Copyright (c) 1993-2015 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling comp7.pas
comp7.pas(13,14) Error: Variable identifier expected
comp7.pas(13,14) Error: Variable identifier expected
comp7.pas(15) Fatal: There were 2 errors compiling module, stopping
Fatal: Compilation aborted
Error: /home/sierra/bin/fpc-3.0.0/bin/ppcx64 returned an error exitcode

Las primeras tres lı́neas del resultado indican la versión del compilador, el copyright, y
el sistema operativo al que se compila. No le prestaremos atención. A partir de la cuarta
lı́nea (Compiling ...) están los mensajes del compilador que nos ayudan a resolver los
problemas. Como se detectaron errores, la penúltima lı́nea nos informa que la compilación
abortó, y la última indica que el compilador avisó del error al sistema operativo. En este
momento solamente nos interesan los mensajes entre las lı́neas cinco y siete que nos ayudan
a arreglar los errores.
La quinta lı́nea informa que en la fila 13 y columna 14 del archivo comp7.pas se encontra-
ron dos errores con el mismo mensaje; ya los hemos resuelto en la primera sección. El último
error reportado en la lı́nea 15 solamente refiere al hecho de que llegó al final del programa
habiendo encontrado dos errores.
El compilador etiqueta cada mensaje como Fatal, Error, Warning, Hint, y Note. Al com-
pilar correctamente no aparecerán ni Fatal ni Error; es bueno entender y eliminar todos
los Warning. Los restantes etiquetados no corresponden a errores ni posibles errores. Pue-
den ver las diferencias entre estas etiquetas en www.freepascal.org/docs-html/current/
user/usersu13.html#x36-430005.1.2.
Como vimos antes, entender los mensajes requiere que busquemos información en la Guı́a
de Usuario.

Compilación sin chequeo de rangos. Si compilamos sin chequeos de rango resultarán


invisibles algunos errores de programación. Por ejemplo, si ejecutamos el problema tres man-
teniendo la desigualdad i <= N en lugar de i <= N - 1, nos encontraremos con la siguiente
situación.

[Complementarios]$ ./comp7

5
1 2 3 4
Desordenado
[Complementarios]$ ./comp7
1 2 4 3
Desordenado
[Complementarios]$ ./comp7
999 888 999 888
Desordenado

Al realizarse la iteración se evalúa la expresión tabla[i] < tabla[i+1] cuando i vale


N. Pero la expresión tabla[N+1] no está definido. Al compilar sin las opciones de chequeo
de rango el uso de esa expresión se toma como válido, aún cuando no está definida. La
ejecución continúa sin error, y obtenemos un resultado incorrecto. El chequeo de rangos no
evita errores, sino que permite que la ejecución de un programa los detecte; al eliminar el
chequeo no se evitan errores, simplemente se impide que el programa los detecte.

También podría gustarte