Está en la página 1de 6

FCI-Adm-4.

01
PONTIFICIA UNIVERSIDAD CATÓLICA DEL PERÚ
FACULTAD DE CIENCIAS E INGENIERÍA

ESTRUCTURA DE DATOS Y PROGRAMACIÓN METÓDICA


Examen 2
(Segundo semestre de 2018)

Horario 0581: prof. V.Khlebnikov


Horario 0582: prof. A.Bello R.
Duración: 3 horas.
Nota: No se puede usar ningún material de consulta.
La presentación, la ortografía y la gramática influirán en la calificación.
Puntaje total: 20 puntos
Pregunta 1 (10 puntos - 80 min.) (Rubin’s Problem) En 1968, el catedrático Edsger W. Dijkstra es-
cribió una carta a los editores de Communications of the ACM, emblemática publicación de la Association
for Computing Machinery (ACM ). Él la tituló "A Case against the GO TO Statement", pero el editor
lo renombró como "Go To Statement Considered Harmful". Este se difundió hasta llegar a ser uno de los
temas más citados en la historia de las revistas.

En 1987, Frank Rubin presentó una carta al editor titulada "’GOTO Considered Harmful’ Considered
Harmful", Rubin argumentaba que la sentencia GOTO a menudo es muy útil. Ésta carta fue publicada
en el ejemplar de marzo de la mencionada revista.

En este examen nos ocuparemos del problema que Rubin presentó para argumentar su posición. A conti-
nuación sus propias palabras:

"Propuse el siguiente problema a un grupo de expertos programadores de computadoras. ’Sea X una


matriz de enteros de NxN. Escriba un programa que imprimirá el número de la primera fila de ceros de
X, si hay alguna’.

Del grupo, tres usaron frecuentemente GOTO en su trabajo. Ellos produjeron siete líneas de código
cercanamente a lo siguiente:

for i:= to n
do begin
for j:= to n do
if x[i,j] <> 0
then goto reject;
writeln('The first all-zero row is',i);
break;
reject:end

Los otros diez programadores normalmente evitaron usar la sentencia GOTO. Ocho de ellos produjeron
13 o 14 líneas de programa usando una bandera para indicar cuando la fila de ceros fue encontrada. (Los
otros dos programadores fueron ya sea incorrectos o hasta más complejos). Lo siguiente es típico de los
programas producidos:

i:=1;
repeat
j:=1;
allzero := true;
while (j <= n) and allzero
do begin
if x[i, j] <> 0
then allzero := false;

1INF06 Estructura de Datos y Examen 2 (2018-2) 1


Programación Metódica
j:= j + 1;
end;
i := i + 1;
until (i > n) or allzero;
if i <= n
then writeln('The first all-zero row is ', i - 1);

Después de revisar varias versiones sin GOTO, me fue posible eliminar la bandera, y reducir el programa
a nueve líneas:

i:=1;
repeat
j:=1;
while (j <= n) and (x[i,j] = 0) do
j := j + 1;
i := i + 1;
until (i > n) or (j > n );
if i > n
then writeln('The first all-zero row is ', i - 1);

Por donde se le mire no hay parcialidad contra la sentencia GOTO, los dos programas que no contienen
GOTO son más complejos que el programa que usa GOTO. A parte de pocas líneas de código, el programa
que usa GOTO tiene solo 13 operadores, comparado con los 21 y 19 que usa los programas sin GOTO,
y solo 41 tokens en total comparado con los 74 y 66 de los otros programas. Mucho más importante, los
programadores que usaron GOTO les tomó menos tiempo llegar a su soluciones.

Por toda mi experiencia me obliga a concluir que es momentos de despedirse del dogma ’no usar GOTO’
en programación. No ha podido demostrar su mérito."
Frank Rubin

Esto ocasionó una serie de comentarios por parte de muchos participantes, incluyendo una respuesta a
estos comentarios por parte del propio Rubin. Finalmente Edsger Dijkstra escribió manifestando que no
había respondido anteriormente con la esperanza de que alguien más hiciese los comentarios que él tenia
pensado. En propias palabras de Dijkstra, no encontró a alguien que expresara lo que el deseaba, a sí que
se apresuró a expresar sus "preocupaciones, grandes y pequeñas".

Entre los puntos enumerados se encuentran las siguientes:

a) (2 puntos - 16 min.) El segundo programa de Rubin falla, debido a un error en una línea. Indique
¿en qué casos falla? ¿cuál es el error en el programa y cómo se debería solucionar? (su respuesta en
dos líneas como máximo).

b) (1 punto - 8 min.) En el tercer programa sugiere evitar el conectivo en la condicional del while
porque complica el razonamiento formal del programa. ¿cuál sería el potencial problema que esconde
y según el desarrollo del curso cómo se debería expresar la condición? (su respuesta en tres líneas
como máximo).

Dijkstra derivó el programa correspondiente, aunque no proporcionó muchos detalles de su desarrollo. Así
que nuestra tarea será explicar su solución.

"El enunciado del problema es: ’sea X una matriz de enteros de NxN. Escriba un programa que imprima
el número de la primera fila de ceros de X, si hay alguna’. Ahora, concentrémonos en empezar con ’si
hay alguna’ del enunciado; nada se debería imprimir si todas las filas tienen un elemento que no es cero;
formalmente, si:

1INF06 Estructura de Datos y Examen 2 (2018-2) 2


Programación Metódica
(∀ i : 0 ≤ i < N : ¬(∀ j : 0 ≤ j < N : X[i, j] = 0))" Dijkstra

Ahora, será el turno de poner en práctica lo aprendido en clase en esta segunda parte del curso. Para
facilitar el razonamiento, añadiremos la variable d, cuyo único objetivo será almacenar el valor de la
expresión de arriba. Entonces nuestra postcondicón será:

{P ost : d = (∀ i : 0 ≤ i < N : ¬(∀ j : 0 ≤ j < N : X[i, j] = 0))}


Nuestro primer paso será obtener la invariante. Elegiremos la técnica de sustituir una constante por una
variable, con lo cual obtenemos:

{Inv : · · · }

c) (1 punto - 8 min.) Escriba la invariante.

Claramente los valores iniciales serán: n, d := 0, true y con función de cota: t : N − n. La protección
del bucle será por supuesto: n 6= N . Ahora como nuestra intención es hacer decrecer la función de cota,
incrementaremos el valor de n en 1 y analizamos el wp.

n, d := 0, true
{Inv : · · · }{t : N − n}
do n 6= N → {Inv ∧ n 6= N }
···
wp(”n := n + 1”, Inv) = P ∧ Q ∧ R
n := n + 1
{Inv}
od

Las expresiones P ∧ Q está garantizada por la invariante y la protección del bucle, pero R no lo está.

d) (1 punto - 8 min.) Determine la expresión R.

Debido a que este término no puede ser derivada de la invariante, trataremos de satisfacerla derivando
un nuevo bucle dentro del bucle en que nos encontramos. Para ello introducimos una segunda variable c
y la derivamos de forma separada.

Si hacemos c = R, entonces la instrucción sugerida es: d := d ∧ c. Por supuesto, a condición de que c se


encuentre garantizada. Por este motivo, lo tomamos como una postcondición que tenemos que satisfacer.
El programa obtenido hasta ahora, es el siguiente:

n, d := 0, true
{Inv}{t : N − n}
do n 6= N → {Inv ∧ n 6= N }
···
{c = R}
d := d ∧ c
n := n + 1
{Inv}
od

Antes de empezar a derivar este aserto, observemos que podemos, por facilidad, calcular primero ¬c.
Luego derivamos el bucle (la invariante se obtiene de forma semejante al primer bucle) y obtenemos el
siguiente programa:

e) (3 puntos - 24 min.) Presente el programa terminado, junto con los asertos correspondientes.

1INF06 Estructura de Datos y Examen 2 (2018-2) 3


Programación Metódica
Al terminar el programa la variable booleana d nos podrá informar si hay, en el vector, alguna fila
con la condición solicitada. Sin embargo, hay dos objeciones al programa. El primero se refiere a que
innecesariamente se recorre todo el vector, aún cuando ya se haya encontrado la fila. Y segundo, debido
a que recorre todo el vector no se podrá determinar la fila, a excepción de que el arreglo no tenga alguna
fila con ceros. Ambos inconvenientes pueden ser resueltos con dos leves modificaciones al programa. El
programa quedaría como se muestra abajo, y es el programa que Edsger Dijstra presentó como respuesta
a Runbin, en ACM Comunnications.

f ) (2 puntos - 16 min.) Presente el programa mejorado y simplificado.

“At the IFIP Congress in 1971 I had the pleasure of meeting Dr. Eiichi Goto of Japan, who cheerfully
complained that he was always being eliminated.” — Donald Knuth, in “Structured Programming with
go to Statements,” Computing Surveys, Vol. 6, No. 4, December 1974.

Pregunta 2 (10 puntos - 1 h. 20 min.) Se pide escribir un procedimiento que permita a justificar
(alinear a la izquierda y a la derecha) las líneas del texto insertando entre las palabras espacios de tal
forma que la última palabra de cada línea se termine en su última columna. Por ejemplo, estas 5 líneas

1...5...10...15...20...25...30...35.
La␣alineación␣a␣la␣derecha␣con␣la␣␣␣
inserción␣de␣los␣espacios␣en␣blanco␣
adicionales␣es␣una␣de␣las␣tareas␣de␣
un␣editor␣de␣palabras,␣como,␣por␣␣␣␣
ejemplo,␣de␣LibreOffice␣Writer␣ver.␣
6.0.6.2␣que␣se␣usa␣en␣Linux␣Mint␣19.

después del ajuste deben tener la forma siguiente:

1...5...10...15...20...25...30...35.
La␣␣alineación␣␣a␣␣la␣derecha␣con␣la
inserción␣de␣los␣espacios␣en␣␣blanco
adicionales␣␣es␣una␣de␣las␣tareas␣de
un␣editor␣␣de␣␣palabras,␣␣como,␣␣por
ejemplo,␣␣de␣LibreOffice␣Writer␣ver.
6.0.6.2␣que␣se␣usa␣en␣Linux␣Mint␣19.

Hay algunas limitaciones para el ajuste. En primero, en una línea, la diferencia entre las cantidades de
espacios entre las palabras no debe ser mayor que 1. En segundo, en las líneas pares (impares) la mayor
cantidad de espacios se inserta, si es necesario, desde la derecha (izquierda). Por ejemplo, para el caso
dado, en la primera línea 3 espacios fueron redistribuidos entre primeras 4 palabras, pero en la siguiente
línea un espacio fue insertado entre la última y la penúltima palabras.

Se necesita derivar una función para el cálculo de los números de columnas donde comienzan las palabras
en la línea justificada a partir de los números de columnas de los inicios de las palabras en la línea aún
no justificada.

En Python 3, la función será justificar


def justificar(n,z,s,b):
donde n es la cantidad de palabras en la línea número z. Las palabras (de 1 a n) comienzan en las colum-
nas b[1], ..., b[n] que son los elementos de la lista b (el elemento b[0] siempre es 0). Cada par de palabras se
separa exactamente por un espacio. El parámetro s proporciona el número total de espacios adicionales
para insertar entre las palabras. El procedimiento calcula los nuevos valores de la lista b.

Especificaremos la precondición:

1INF06 Estructura de Datos y Examen 2 (2018-2) 4


Programación Metódica
La línea de entrada tiene el formato de una secuencia de palabras (W i) con los espacios entre ellas:
W 1 [1] W 2 [1] ... [1] W n [s]
donde en corchetes se indica la cantidad de espacios correspondientes al lugar indicado.
La precondición será Q : 0 ≤ s ∧ 0 ≤ n ∧ b[0:n+1] = B[0:n+1] (notación de Python 3).
La línea de salida tiene el formato de
W 1 [p+1] ... [p + 1] W t [q + 1] ... [q + 1] W n.

a) (1 punto - 8 minutos) Para la línea 1 del ejemplo dado, ¿cuáles son los valores de n, s, B, p, t, q?

La especificación completa de la función podría ser la siguiente:

{Q}
Calcular p, q, t para establecer Q1;
{ Q ∧ Q1 }
Calcular los nuevos valores de b[0:n+1] para establecer R
{ Q1 ∧ R }
donde
Q1 : 1 ≤ t ≤ n ∧ 0 ≤ p ∧ 0 ≤ q ∧ p∗(t−1)+q∗(n−t) = s ∧ (par(z) ∧ q = p+1 ∨ impar(z) ∧ p = q+1)
y
R : (∀i : 1 ≤ i ≤ t : b[i] = B[i]+p∗(i−1)) ∧ (∀i : t < i ≤ n : b[i] = B[i]+p∗(t−1) + q∗(i−t)).

b) (1 punto - 8 minutos) Explique detalladamente el significado de cada cláusula en los asertos Q1


y R.

En el cálculo de p, q, t se puede llegar a


1 ≤ t ≤ n ∧ 0 ≤ p ∧ p∗(n−1)+n−t = s.
c) (2 puntos - 16 minutos) ¿Cómo?
con la solución obvia:

p = s div (n−1), n−t = s mod (n−1)


d) (0,5 puntos - 4 minutos) ¿Cuál es el problema que surge aquí y cuál es su solución?
Si tomamos el cálculo como
p = s div (n−1)
t = n − (s mod (n−1))
q =p+1
entonces debe ser que
Q ∧ par(z) ⇒ pmd(las tres asignaciones de p, q y t; Q1),
o, simplificándolo,
Q ∧ par(z) ⇒ 1 ≤ n−(s mod (n−1)) ≤ n ∧ 0 ≤ s div (n−1) ∧ par(z).
Lo que se puede demostrar fácilmente.

e) (1,5 puntos - 12 minutos) Ahora se puede completar el cálculo de p, q, t para otro caso.

Pasamos al cálculo de los nuevos valores de la lista b.


Usaremos dos bucles. Cada bucle se construye por la creación primero del invariante, después los mandatos
del bucle y, al final, la protección correspondiente.
Calculamos primero b[t+1:n+1] (notación en Python 3) con

{ Inv : t ≤ k ≤ n ∧ e = p∗(t−1)+q∗(k−t) ∧ b[1:k+1] contiene los valores iniciales ∧


b[k+1:n+1] contiene los valores finales
Dec : k − t }

El bucle construido se puede observar en la versión completa del programa que está presentada más
adelante.

1INF06 Estructura de Datos y Examen 2 (2018-2) 5


Programación Metódica
f ) (1 punto - 8 minutos) ¿Qué aserto será cierto al salir del primer bucle?

En el segundo bucle b[1:t+1] (notación en Python 3) con

{ Inv : 1 ≤ k ≤ t ∧ e = p∗(k−1) ∧ b[1:k+1] contiene los valores iniciales ∧


b[k+1:n+1] contiene los valores finales
Dec : k − 1 }

El programa final será el siguiente:

def justificar(n,z,s,b):
assert(n>1)
# Calculation of p, q, t
if z & 1:
q= s // (n-1)
t= 1 + (s % (n-1))
p= q+1
else:
p= s // (n-1)
t= n - (s % (n-1))
q= p+1
# New columns calculation
k= n; e= s
while k != t:
b[k]+= e; k-= 1; e-= q
while e != 0:
b[k]+= e; k-= 1; e-= p

g) (3 puntos - 24 minutos) Comprobar (1) que los invariantes se cumplen al comienzo de los bucles,
(2) que los cuerpos de los bucles mantienen los invariantes, y (3) que los invariantes y los cierres de
protecciones garantizan las postcondiciones de los bucles.

Preparado por AB(1) y VK(2) con LATEX


en Linux Mint 19 Tara
Profesores del curso: V.Khlebnikov
A.Bello R.
Pando, 11 de diciembre de 2018

1INF06 Estructura de Datos y Examen 2 (2018-2) 6


Programación Metódica

También podría gustarte