La Programación Imperativa

Anuncio
C APÍTULO 13
La Programación Imperativa
Índice del Capítulo
13.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
13.2. Especificaciones de programas . . . . . . . . . . . . . . . . . . . . . . . . 220
13.3. La sentencia skip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
13.4. La sentencia abort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
13.5. La sentencia de asignación . . . . . . . . . . . . . . . . . . . . . . . . . . 222
13.5.1. Concatenación o composición . . . . . . . . . . . . . . . . . . . . . 225
13.6. Cálculo de partes de asignaciones . . . . . . . . . . . . . . . . . . . . . . 226
13.7. Sentencias y expresiones condicionales . . . . . . . . . . . . . . . . . . . . 228
13.7.1. La sentencia alternativa . . . . . . . . . . . . . . . . . . . . . . . . . 229
13.8. Expresiones condicionales . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
13.9. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
13.1. Introducción
A partir de ahora cambiaremos el modelo computacional y por lo tanto el formalismo para
expresar los programas. Todo lo hecho hasta ahora nos será de utilidad en lo que sigue, ya que
la experiencia adquirida en el cálculo de programas, y la consecuente habilidad para el manejo
de fórmulas, seguirá siendo escencial en este caso, mostrando así que pese a sus diferencias, la
programación funcional y la imperativa tienen varias cosas en común.
219
220
13. L A P ROGRAMACIÓN I MPERATIVA
13.2. Especificaciones de programas
Vamos ahora a presentar algunas de las aplicaciones del cálculo de predicados en computación: las especificaciones formales. En el estilo de la programación imperativa, la computación
no se expresa como cálculo de valores como vimos en el modelo de la programación funcional, sino como modificación de estados. Para desarrollar programas imperativos, es por lo tanto
deseable expresar las especificaciones como relaciones entre estados iniciales y finales.
Así es que un problema a resolver en el modelo de la programación imperativa estará especificado por un espacio de estados, una precondición y una poscondición. El espacio de estados
es el conjunto de valores que pueden tomar las variables. La pre y poscondición son predicados
que expresan el problema dado en términos precisos.
La notación que usaremos para expresar la especificación de un programa imperativo es:
{P } S {Q}
donde P y Q representan a los predicados que describen la pre y poscondición del programa
respectivamente y S es un programa, también llamado sentencia, instrucción o comando.
La terna {P } S {Q} se denomina tripleta de Hoare y tiene el siguiente significado:
Siempre que se ejecuta S comenzando en un estado que satisface P , se termina en
un estado que satisface Q.
Recordemos que vimos en el Capítulo 1 el concepto de tripleta de Hoare {P } S {Q}
cuando S es una asignación.
Podemos afirmar, entonces que derivar un programa consiste en determinar S de manera
que satisfaga las especificaciones dadas.
Por ejemplo, derivar un programa que satisfaga {P } S {x = y}, consiste en encontrar S tal
que, después de su ejecución sobre un estado que satisface P se obtiene un estado que satisface
x = y.
En los lenguajes imperativos, es necesario explicitar el tipo de las variables y constantes involucradas en el programa, lo cual se hace a través de una declaración de variables y constantes.
Esto completa la especificación del programa a la vez que indica implícitamente las operaciones
que se pueden realizar con las variables y constantes declaradas.
Así es que un problema a resolver en el modelo de la programación imperativa estará especificado por un espacio de estados, una precondición y una poscondición. El espacio de estados
es el conjunto de valores que pueden tomar las variables. La pre y poscondición son predicados
que expresan el problema dado en términos precisos.
Por ejemplo, consideremos las especificaciones de un programa que calcula el máximo
común divisor entre dos números enteros positivos X e Y,
13.2. E SPECIFICACIONES
DE PROGRAMAS
221
|[var x, y : Int
cons X,Y : Int
{X > 0 ∧ Y > 0 ∧ x = X ∧ y = Y}
S
{x = mcd.X.Y}
]|
La primera línea define el espacio de estados Z × Z. La segunda línea corresponde a la
precondición del programa, las variables X e Y son llamadas variables rígidas, no son variables
del programa y por tanto no deben aparecer en las sentencias del programa. La tercera línea
corresponde a las sentencias del programa especificado y finalmente la cuarta contiene a la
poscondición del programa. La interpretación operativa de las especificaciones es la que sigue:
el programa S satisface las especificaciones, si para todos los enteros X e Y, la ejecución de S en
un estado que satisface {X > 0 ∧ Y > 0 ∧ x = X ∧ y = Y}, termina en un estado que satisface
{x = mcd.X.Y}.
Antes de continuar con el desarrollo de programas, veremos algunas reglas para la correcta
interpretación y utilización de {Q} S {R}, aún cuando S no sea una asignación.
La primera regla, conocida como ley de exclusión de milagros, expresa:
{P } S {f alse} ≡ P ≡ f alse
Esta regla establece que para cualquier programa S, si se requiere que termine y que los
estados finales satisfagan f alse, entonces ese programa no puede ejecutarse exitosamente para
ningún posible estado inicial. La regla de exclusión de milagros establece que, cada vez que se
ejecuta S comenzando en un estado que satisface P y terminando en uno que satisface f alse,
(es decir en ningún estado), P sea equivalente a f alse.
La expresión
{P } S {true}
indica que, cada vez que comienza en un estado que satisface P , la ejecución de S concluye
en un estado que satisface el predicado true, es decir, la ejecución de S termina cada vez que
se comienza en un estado que satisface P .
Otra regla que introducimos a continuación está relacionada con el hecho de que una precondición puede ser fortalecida y una poscondición puede ser debilitada, lo cual formulamos de
la siguiente manera:
{P } S {Q} ∧ (Po ⇒ P ) ⇒ {Po } S {Q}
{P } S {Q} ∧ (Q ⇒ Qo ) ⇒ {P } S {Qo }
Supongamos que las tripletas {P } S {Q} y {P } S {R} son válidas. Entonces la ejecución
de S en un estado que satisface P termina en uno que satisface Q y R, entonces termina en un
estado que satisface Q ∧ R. Esta regla se exprersa de la siguiente manera:
222
13. L A P ROGRAMACIÓN I MPERATIVA
{P } S {Q} ∧ {P } S {R} ≡ {P } S {Q ∧ R}
Por último, la regla que sigue
13.5. L A
223
SENTENCIA DE ASIGNACIÓN
Esta definición fue dada bajo el supuesto de que E es total, es decir, E asume un valor en
cualquier estado. Muchas expresiones no son totales, por ejemplo, 10/x está definida sólo si
x 6= 0, y la referencia de lista c [i] está definida sólo si i está dentro del rango de la lista c.
Para cada expresión E, definimos el predicado
{P } S {Q} ∧ {R} S {Q} ≡ {P ∨ R} S {Q}
dom.E
que se satisface exactamente en aquellos estados para los cuales E está definida. (dom.E
proviene de “dominio” de E). Veamos algún ejemplo:
13.3. La sentencia skip
La primer sentencia que consideramos es la sentencia skip. La ejecución de skip no produce
cambios sobre los estados de las variables. La interpretación operativa de esta sentencia en
términos de la tripleta de Hoare es
p
dom. x/y ≡ y 6= 0 ∧ x/y ≥ 0.
Damos ahora una definición más general de asignación:
{dom.E ∧ R [x := E]}
{P } skip {P }
Ahora como la precondición puede ser fortalecida entonces también podemos caracterizar
esta sentencia mediante:
{P } skip {Q} ≡ P ⇒ Q
Es decir, la precondición X más débil que hace válida la tripleta {X} skip {Q} es Q.
Por ejemplo, la tripleta {x ≥ 1} skip {x ≥ 0} es válida pues x ≥ 1 ⇒ x ≥ 0.
{R} .
(13.1)
En lo que sigue, omitiremos dom.E en la precondición cuando tratemos la sentencia de
asignación de modo de simplificar la escritura.
Afirmaremos sin demostrarlo que R [x := E] es la precondición más débil que al ejecutar
x := E termina con el resultado R ≡ true. Esto significa que otra precondición, digamos Q,
satisface {Q} x := E {R} si y sólo si Q ⇒ R [x := E]. Por lo tanto, tenemos la siguiente
regla para la sentencia de asignación:
(13.1)
Método de prueba. {Q}
x := E
{R} ≡ Q ⇒ R [x := E].
Consideremos la especificación {x > 0} x :=? {x > 1}. Queremos probar que esta
especificación es implementada por la asignación x := x + 1, entonces probamos:
13.4. La sentencia abort
x > 0 ⇒ (x > 1) [x := x + 1] ,
La interpretación operativa de esta sentencia está dada por la siguiente regla:
{P } abort {Q} ≡ P ≡ f alse
Después de esta regla, cómo se ejecuta abort?. La expresión de arriba establece que este
comando nunca debe ejecutarse, ya que sólo puede hacerse en un estado que satisface f alse,
y no existe tal estado. Si la ejecución de un programa alcanza alguna vez un punto en el que
abort deba ejecutarse, entonces el programa completo es un error, y termina prematuramente.
(x > 1) [x := x + 1]
hdefinición de sustitucióni
x+1>1
=
haritméticai
x>0
=
Veamos una situación diferente: Encontrar una expresión E de modo que satisfaga
Cualquier cambio de estado que ocurra durante la ejecución de un programa se debe a la
sentencia de asignación.
En el capítulo 1, definimos asignación múltiple x := E mediante el axioma
x := E
suponiendo el antecedente y probando el consecuente:
La última línea es el antecedente que habíamos supuesto.
13.5. La sentencia de asignación
{R [x := E]}
x := E
{R} .
{true}
x, y := x + 1, E
{y = x + 1}
Para lo cual E debe satisfacer:
true ⇒ (y = x + 1) [x, y := x + 1, E] ,
224
13. L A P ROGRAMACIÓN I MPERATIVA
lo cual significa que E debe cumplir:
13.5. L A
Ahora probaremos que se verifica 0 ≤ i ≤ n ∧ i = I + 1, es decir, vale:
true ⇒ (y = x + 1) [x, y := x + 1, E]
hCálculo de predicados (true ⇒ R) ≡ Ri
(y = x + 1) [x, y := x + 1, E]
=
hsustitucióni
E =x+1+1
=
haritméticai
E =x+2
P ∧ I = i 6= n ⇒ (0 ≤ i ≤ n ∧ i = I + 1) [x, i := x + b [i] , i + 1]
=
Nuevamente, suponemos el antecedente y probamos el consecuente:
Ahora supongamos un algoritmo que suma los elementos de la lista b [0, . . . , n − 1]. Consideremos el siguiente predicado
X
P :0≤i≤ n∧x =
k : 0 ≤ k < i : b [k]
que establece que x es la suma de los primeros i elementos de b [0, . . . , n − 1].
Queremos probar que
x, i :=?
(0 ≤ i ≤ n ∧ i = I + 1) [x, i := x + b [i] , i + 1]
hSustitucióni
0≤i+1≤n∧i+1=I+1
=
hSuposición de i = I y (3.39) Neutro de ∧i
0≤i+1≤n
=
hSuposición de i 6= n y aritmética i
0≤i≤n
=
Es decir, E debe ser una expresión aritmética que evaluada en cualquier estado resulte equivalente a la expresión aritmética x + 2, lo que significa que podemos dar como respuesta esta
última expresión.
{P ∧ I = i 6= n}
225
SENTENCIA DE ASIGNACIÓN
{P ∧ i = I + 1}
(13.2)
se implementa mediante x, i := x + b [i] , i + 1.
Primero, discutiremos el contexto en el cual la especificación (13.2) puede aparecer. Esta
especifica el cuerpo de un bucle que acumula la suma de los elementos de b [0, . . . , n − 1]. El
requerimiento correspondiente a que el valor de i se incremente en una unidad asegura que el
bucle progresa hacia su terminación. El requerimiento acerca de que P se mantiene, es decir
si es true antes de la iteración, también lo será luego, asegura que cuando finaliza el bucle, x
contiene la suma de los primeros n elementos de b.
Probaremos
ahora, que x, i := x + b [i] , i + 1 verifica la segunda conjunción de P ,
P
x = ( k : 0 ≤ k < i : b [k]), es decir:
X
P ∧ I = i 6= n ⇒ (x = (
k : 0 ≤ k < i : b [k])) [x, i := x + b [i] , i + 1]
La prueba asegura el antecedente y prueba el consecuente:
P
(x = ( k : 0 ≤ k < i : b [k])) [x, i := x + b [i] , i + 1]
=
hSutitucióni
P
x + b [i] = ( k : 0 ≤ k < i + 1 : b [k])
=
hSeparación
P de término (5.21)i
x + b [i] = ( k : 0 ≤ k < i : b [k]) + b [i]
=
hSuposición de P i
x + b [i] = x + b [i]
=
h(3.3) Neutro de ≡i
true
La última línea es una conjunción de la suposición P .
13.5.1. Concatenación o composición
La concatenación permite escribir una secuencia de instrucciones. La ejecución de dos sentencias S y T , una a continuación de la otra se indicará separando las mismas con punto y coma:
S; T , siendo la de la izquierda la primera que se ejecuta. Para demostrar {P } S; T {Q}, hay
que encontrar un predicado intermedio R que sirva como poscondición de S y precondición de
T , es decir:
{P }
S; T
{Q} ≡ (∃R :: {P }
S
{R} ∧ {R}
T
{Q})
La precondición más débil de la concatenación S; T con respecto al predicado Q, se obtiene
tomando primero la precondición más débil de T con respecto a Q y luego la precondición más
débil de S con respecto a este último predicado.
Por ejemplo, supongamos que queremos encontrar la precondición más débil tal que la ejecución de la secuencia x := E y y := F de asignaciones termine con R ≡ true:
{?}
x := E; y := F
{R}
Sabemos cómo encontrar la precondición más débil tal que y := F termine en R ≡ true:
R [y := F ], entonces podemos encontrar la precondición más débil de modo que la ejecución
de x := E verifique R [y := F ], que es R [y := F ] [x := E]. Ilustraremos esto a continuación,
la primera columna presenta dos asignaciones y la poscondición, la segunda muestra el cálculo
de la sentencia intermedia y la tercera presenta la precondición calculada:
x := E
y := F
{R}
x := E
{R [y := F ]}
y := F
{R}
{R [y := F ] [x := E]}
x := E
{R [y := F ]}
y := F
{R}
226
13. L A P ROGRAMACIÓN I MPERATIVA
Este método puede generalizarse para encontrar la precondición más débil correspondiente
a una secuencia de asignaciones de modo de verificar R:
{R [xn := En ] . . . R [x2 := E2 ] R [x1 := E1 ]}
x1 := E1 ; x2 := E2 ; . . . ; xn := En
{R}
(x = X ∧ y = Y) [y := t] [x := y] [t := x]
hSustitucióni
(x = X ∧ t = Y) [x := y] [t := x]
=
hSustitucióni
(y = X ∧ t = Y) [t := x]
=
hSustitucióni
(y = X ∧ x = Y)
=
Veamos otro ejemplo, queremos resolver e en
n
o
X
P2 : x = (
k : i ≤ k ≤ n : b [k])
i, x := i − 1, e
{P 2}
P 2 [i, x := i − 1, e]
hDefinición
de P 2 Sustitucióni
P
e = ( k : i − 1 ≤ k ≤ n : b [k])
=
hSeparación dePtérmino (5.21)i
e = b [i − 1] + ( k : i ≤ k ≤ n : b [k])
=
hSuposición de P 2i
e = b [i − 1] + x
=
Con lo cual podemos usar la expresión b [i − 1] + x por e.
Existe otra forma de resolver e en
Hemos probado que la secuencia de asignaciones intercambia x con y.
13.6. Cálculo de partes de asignaciones
Supongamos que queremos mantener:
X
P1 : x = (
k : 0 ≤ k ≤ i : b [k])
usando como asignación i, x := i + 1, e donde e es un valor desconocido.
Veremos cómo podemos obtener e a través de un cálculo, en vez de obtenerlo por tanteo.
Queremos resolver e en:
i, x := i + 1, e
227
DE PARTES DE ASIGNACIONES
Para ello debemos resolver e en P 2 ⇒ P 2 [i, x := i − 1, e].
Por ejemplo, busquemos la precondición más débil de modo que la ejecución de t := x;
x := y; y := t verifique x = X ∧ y = Y, entonces:
{P 1}
13.6. C ÁLCULO
{P 1}
Esta tripleta de Hoare es válida exactamente cuando P 1 ⇒ P 1 [i, x := i + 1, e], de modo
que debemos resolver esta expresión booleana para e. Supondremos para esto el antecedente y
probaremos el consecuente:
P 1 [i, x := i + 1, e]
=
hDefinición
de P 1 y Sustitucióni
P
e = ( k : 0 ≤ k ≤ i + 1 : b [k])
=
hSeparación
de término (5.21)i
P
e = ( k : 0 ≤ k ≤ i : b [k]) + b [i + 1]
=
hSuposición de P 1i
e = x + b [i + 1]
Por lo tanto, podemos usar la expresión x + b [i + 1] por e.
P ⇒ P [i, x := f, e]
donde f es una expresión en i. Observemos que el consecuente tiene la misma estructura que
el antecedente, salvo que, mientras el antecedente tiene variables como i y x el consecuente tiene
expresiones. Por lo tanto, si podemos manipular el antecedente P hasta que tenga la estructura
necesaria que contenga expresiones en donde se encontraban i y x, habremos identificado e.
Veamos un ejemplo, consideremos la función F definida como:
F. 0 = 0
F. 1 = 1
F (2 × n) = F.n
para n par y mayor que 0
F (2 × n + 1) = F.n + F (n + 1) para n impar y mayor que 0
Consideremos el predicado
P : C = a × F.n + b × F (n + 1)
y supongamos que queremos encontrar d y e de modo que satisfagan
{P ∧ n > 0 ∧ even.n}
n, a, b := n ÷ 2, d, e
{P }
Lo cual exige resolver d y e en P ∧ n > 0 ∧ even.n ⇒ P [n, a, b := n ÷ 2, d, e]. Esto último
puede reescribirse de acuerdo al teorema 3.64 (p ∧ q ⇒ r ≡ p ⇒ (q ⇒ r)) de la siguiente
manera:
n > 0 ∧ even.n ⇒ (P ⇒ P [n, a, b := n ÷ 2, d, e])
Supondremos el antecedente n > 0 ∧ even.n. Para simplificar los cálculos, llamemos k =
n ÷ 2. Trabajaremos sobre P con el objeto de llegar a una expresión con la misma estructura
pero con n ÷ 2 en vez de n.
228
13. L A P ROGRAMACIÓN I MPERATIVA
P
=
=
=
=
=
13.7. S ENTENCIAS
(13.3)
hDefinición de P i
C = a · F.n + b · F (n + 1)
hSuposición de even.n por lo tanto n = 2 · ki
C = a · F (2 × k) + b × F (2 × k + 1)
hDefinición de F dos vecesi
C = a × F.k + b × (F.k + F.(k + 1))
hAritméticai
C = (a + b) × F.k + b × F.(k + 1)
hDefinición de k; Sustitucióni
P [n, a, b := n ÷ 2, a + b, b]
Y EXPRESIONES CONDICIONALES
229
Ejemplo. Queremos probar:
{true}
if B
then skip else x, y := y, x
{x ≤ y}
Usamos el método de prueba 13.2, es decir debemos probar:
{true ∧ x ≤ y} skip {x ≤ y}
{true ∧ ¬(x ≤ y)} x, y := y, x {x ≤ y}
Por lo tanto, hemos resuelto d y e, y la asignación deseada es n, a, b := n ÷ 2, a + b, b que
puede escribirse como n, a := n ÷ 2, a + b.
cuya demostración es inmediata.
13.7.1. La sentencia alternativa
La sentencia if B then S1 else S2 puede escribirse en la notación de guardas como la
sentencia alternativa siguiente:
13.7. Sentencias y expresiones condicionales
La sentencia condicional, llamada IF , tiene la siguiente forma en muchos lenguajes de
programación imperativos:
IF : if B then S1 else S2
(13.3)
donde B es una expresión booleana y S1 y S2 son sentencias.
La sentencia condicional se ejecuta de la siguiente forma:
Si B es true entonces se ejecuta S1, de lo contrario se ejecuta S2.
Supongamos que queremos ejecutar una sentencia condicional que comenzó en un estado
que satisface el predicado {Q} para luego satisfacer el predicado {R}, es decir : {Q} IF {R}.
¿Qué debe ocurrir para poder garantizar que {Q} IF {R} es válida? Si B es true, entonces
debe ejecutarse S1, y por lo tanto la ejecución de S1 debe verificar R. Por otra parte si B es
f alse debe ejecutarse S2, y la ejecución de S2 debe verificar R.
Escribiremos esto de modo de ilustrar la sentencia condicional IF y además indicar que B
y ¬B pueden ser supuestos antes de S1 y S2 respectivamente:
{Q}
if B then {Q ∧ B} S1 {R}
else {Q ∧ ¬B} S2 {R}
{R}
Por lo tanto, tenemos el siguiente:
(13.2)
if
B → S1
[] ¬B → S2
fi
Método de prueba. Método de Prueba para IF : Para probar {Q} IF {R}, es suficiente con probar {Q ∧ B} S1 {R} y {Q ∧ ¬B} S2 {R}.
Un comando de la forma B → S se llama comando protegido o resguardado. B es la
guarda o protección en la entrada →, que asegura se ejecutará el comando S sólo cuando sea
apropiado. En la notación de comando de guarda, una sentencia alternativa puede escribirse con
más de dos posibilidades. Por ejemplo, a continuación presentamos una sentencia alternativa
que llamaremos IF G, con tres comandos guarda:
if
B1 → S1
[] ¬B2 → S2
[] ¬B3 → S3
fi
La ejecución de una sentencia alternativa se realiza de la siguiente manera: si ninguno de
las guardas es true, la ejecución aborta. Abortar significa terminar prematuramente. Cuando un
programa aborta, no se conoce lo que pueda ocurrir, aunque una buena implementación siempre
dará un mensaje de error y detendrá la ejecución. Si al menos una guarda es true, entonces se
elige una de éstas y se ejecuta su comando correspondiente.
Existen dos claves para la sentencia alternativa:
• La ejecución aborta si ninguna guarda es true.
• Si más de una guarda es true, se elige una entre ellas arbitrariamente, y su correspondiente
comando se ejecuta a continuación.
230
13. L A P ROGRAMACIÓN I MPERATIVA
Si más de una guarda es true, la sentencia alternativa se dice no determinística. El “nodeterminismo”, es útil para escribir con claridad determinados programas y también para permitir la simetría. Por ejemplo, en el siguiente programa no es importante cuál entre x e y es
guardado en z cuando x = y, con lo cual el programador no necesita hacer la elección:
if
x ≤ y → z := y
[]
y ≤ x → z := x
fi
{z es el máximo entre x e y}
Otro ejemplo, aquí presentamos un algoritmo que ordena las variables w, x, y, z intercambiando repetidamente sus valores, sería mucho más engorroso escribir este programa sin nodeterminismo:
while
¬(w ≤ x ≤ y ≤ z)
if
w>x
[]
x>y
[]
y>x
fi
{w ≤ x ≤ y ≤ z}
do
→ w, x := x, w
→ x, y := y, x
→ y, z := z, y
Para probar {Q} IF G {R} es suficiente probar que: (i) cuando comienza la ejecución, al
menos una guarda es true y (ii) cada comando verifica R.
(13.4)
Método de prueba. Método de prueba para IF G: Pra probar {Q} IF G {R} es
suficiente probar que:
(a) Q ⇒ B1 ∨ B2 ∨ B3,
13.7. S ENTENCIAS
231
Y EXPRESIONES CONDICIONALES
(a) x = 0 ⇒ true ∨ true,
(b) {x = 0 ∧ true} x := 1 {x = 1 ∨ x = −1},
(c) {x = 0 ∧ true} x := −1 {x = 1 ∨ x = −1}.
El apartado (a) sigue inmediatamente del teorema P ⇒ true ≡ true.
Para demostrar (b), tenemos que demostrar que
x = 0 ∧ true ⇒ (x = 1 ∨ x = −1)[x := 1]
Comenzaremos con el consecuente:
(x = 1 ∨ x = −1)[x := 1]
hSustitucióni
1 = 1 ∨ 1 = −1
=
h(1 = 1) ≡ true y además true es absorbente para ∨i
true
⇐
hPor apartado (a) x = 0 ⇒ truei
x=0
=
htrue neutro de ∧i
x = 0 ∧ true
=
El apartado (c) se resuelve en forma similar.
Veamos otra situación. Queremos determinar S de modo que se satisfaga:
{true}
S
{z = max(x, y)}
Vamos a expresar el máximo entre x e y en una forma que sea más fácil de manipular:
(b) {Q ∧ B1} S1 {R},
(c) {Q ∧ B2} S2 {R} y
z = max(x, y) ≡ (z = x ∨ z = y) ∧ z ≥ x ∧ z ≥ y
(d) {Q ∧ B3} S3 {R}.
Esto significa que una candidato posible para el máximo es el valor de x, por tanto una
instrucción posible a ejecutar es z := x, veamos entonces cuál es la precondición más débil
para la sentencia de asignación z := x respecto del predicado z = max(x, y):
Este método se extiende obviamente a comandos alternativos con más de tres o con menos
de tres comandos guarda.
Veamos ahora algunos ejemplos. Supongamos que queremos demostrar la corrección del
siguiente programa:
{x = 0}
if
true →
x := 1
[]
true → x := −1
fi
{x = 1 ∨ x = −1}
Utilizando el método de prueba 13.4, debemos probar:
((z = x ∨ z = y) ∧ z ≥ x ∧ z ≥ y)[z := x]
hSustitucióni
(x = x ∨ x = y) ∧ x ≥ x ∧ x ≥ y)
=
h(x = x) ≡ true y además (x ≥ x) ≡ truei
(true ∨ x = y) ∧ true ∧ x ≥ y)
=
htrue neutro de ∧ y absorbente de ∨i
x≥y
=
Lo cual demuestra que si elegimos el predicado x ≥ y, la tripleta {x ≥ y}
es válida.
z := x
{z = max(x, y)}
232
13. L A P ROGRAMACIÓN I MPERATIVA
Por simetría también será válida {y ≥ x}
Además, observando que
z := y
{z = max(x, y)}.
x≥y∨y ≥x
haritméticai
x ≥ y ∨ y > x)
=
h tercero excluido p ∨ ¬p ≡ truei
true
⇐
es fácil ver que true ⇒ x ≥ y ∨ y ≥ x
Por lo tanto y utilizando el método de prueba 13.4, podemos afirmar que la sentencia S
puede ser:
if x ≥ y → z := x
[] y ≥ x → z := y
fi
Ahora presentamos otra situación, supongamos que debemos demostrar la corrección del
siguiente programa:
|[var x, y : Int
{true}
x, y := y × y, x × x
if
x ≥ y → x := x − y
[]
y ≥ x → y := y − x
fi
{x ≥ 0 ∧ y ≥ 0}
Observemos que aparecen dos sentencias concatenadas, la primera es una asignación y la
segunda es una sentencia alternativa, por lo tanto, debemos encontra un predicado intermedio Q
que sea la poscondición de la asignación y a su vez la precondición de la alternativa. Comenzamos buscando entonces la precondición más débil respecto de la poscondición {x ≥ 0 ∧ y ≥ 0}.
Utilizando el método de prueba 13.4, debemos encontar Q tal que:
(a) Q ⇒ x ≥ y ∨ y ≥ x,
(b) {Q ∧ x ≥ y} x := x − y {x ≥ 0 ∧ y ≥ 0},
(c) {Q ∧ y ≥ x} y := y − x {x ≥ 0 ∧ y ≥ 0}.
El apartado (a) sigue inmediatamente del teorema Q ⇒ true ≡ true. Los apartados (b) y
(c) imponen condiciones sobre el predicado Q. Comencemos con (b): el método de prueba para
asiganciones, exige que Q satisfaga:
Q ∧ x ≥ y ⇒ (x ≥ 0 ∧ y ≥ 0)[x := x − y],
13.8. E XPRESIONES
CONDICIONALES
233
lo cual equivale a calcular primero la precondición más débil respecto de la asignación
x := x − y
(x ≥ 0 ∧ y ≥ 0)[x := x − y]
hsustitucióni
x−y ≥ 0∧y ≥0
=
haritméticai
x≥ y∧y ≥0
=
Observemos que si suponemos el antecedente Q ∧ x ≥ y, en la última línea, obtenemos
x≥ y∧y ≥0
hsuponiendo el antecedente x ≥ y ≡ truei
true ∧ y ≥ 0
=
htrue neutro de ∧i
y≥0
=
Así conseguimos que Q ⇒ y ≥ 0.
En forma similar y de acuerdo al apartado (c), Q debe satisfacer Q ⇒ x ≥ 0. Resumiendo,
hemos obtenido que Q es cualquier expresión booleana que implique x ≥ 0 ∧ y ≥ 0. Podemos
considerar obviamente a ésta como Q.
Ahora falta ver que true ⇒ Q[y, x := x × x, y × y].
Para esto recordemos que true ⇒ p ≡ p.
Probemos entonces Q[y, x := x × x, y × y]:
(x ≥ 0 ∧ y ≥ 0)[y, x := x × x, y × y]
hsustitucióni
x×x≥0∧y×y ≥0
=
hteoremas de orden i
true
=
13.8. Expresiones condicionales
Así como tenemos sentencias condicionales if B then S1 else S2, también tenemos las
expresiones condicionales:
if B then E1 else E2
(13.4)
donde B es una expresión booleana, y E1 y E2 son expresiones del mismo tipo. Evaluar
una expresión como ésta da como resultado el valor de E1 si B es true y el valor de E2 en otro
caso.
Veamos ejemplos de expresiones condicionales: aquí consideramos un estado en el cual
x = 5, y = 4, b = true y c = f alse, en ese estado tenemos:
234
13. L A P ROGRAMACIÓN I MPERATIVA
(a) (if x = y then x else x + 2) = 7
(b) (if x 6= y then x else x + 2) = 5
(c) (if b ∨ c then x × y else x + 2) = 20
(d) (if b ∧ c then x × y else x + 2) = 7
(e) (if b then c ⇒ b else b ⇒ c) = true
(f) (if c then c ∨ b else b ∧ c) = f alse
Existen dos reglas para manipular expresiones if-then-else:
(13.5)
Axioma. Condicional: B ⇒ ((if B then E1 else E2) = E1)
(13.6)
Axioma. Condicional: ¬B ⇒ ((if B then E1 else E2) = E2)
13.9. Ejercicios
13.1 Dar el significado operativo de las tripletas: {true} S {true} y {f alse} S {true}
13.2 Deducir a partir de las reglas dadas en la Sección 13.2 que
{P0 } S {Q0 } y {P1 } S {Q1 }
implican
{P0 ∧ P1 } S {Q0 ∧ Q1 } y {P0 ∨ P1 } S {Q0 ∨ Q1 }
13.3 Demostrar que {f alse} S {P } es válida para cualquier S y cualquier P .
13.4 Dada una sentencia S y un predicado Q, llamaremos wp.S.Q al predicado más débil P
para el cual la tripleta {P } S {Q} es válida. Este predicado wp.S.Q es llamado la precondición más débil de S respecto de Q. La relación entre {P } S {Q} y wp.S.Q es la
siguiente:
{P } S z {Q} ≡ P ⇒ wp.S.Q
Demostrar que las reglas vistas en la sección 13.2, siguen de las siguientes reglas para
wp.S:
13.9. E JERCICIOS
13.5 Demostrar que las siguientes tripletas son válidas:
|[var x, y : Int
{x > 0 ∧ y > 0}
a) skip
{x > 0}
]|
|[var x, y : Int
{x > 0 ∧ y > 0}
b) skip
{y ≥ 0}
]|
|[var x, y : Bool
{x ≡ y}
c) skip
{x ⇒ y}
]|
13.6 Demostrar que las siguientes tripletas no son válidas:
|[var x, y : Int
{x > 0 ∧ y > 0}
a) skip
{x = 1}
]|
|[var x, y : Int
{x > 0 ∧ y > 0}
b) skip
{y ≥ x}
]|
|[var x, y : Bool
{x ≡ y}
c) skip
{x ∧ y}
]|
13.7 Determinar la precondición más débil P en cada caso:
a) wp.S.f alse ≡ f alse
a) {P } x := x + 1 {x > 0}
b) wp.S.Q ∧ wp.S.R ≡ ws.S.(Q ∧ R)
b) {P } x := x ∗ x {x > 0}
c) wp.S.Q ∨ wp.S.R ⇒ ws.S.(Q ∨ R)
c) {P } x := x ∗ x ∗ x − 2 ∗ x + 4 {x > 0}
235
236
13. L A P ROGRAMACIÓN I MPERATIVA
d) {P } x := x + 1 {x3 − 5x2 + 2x > 0}
3
13.9. E JERCICIOS
13.12 Usar el método (13.2) para probar que el siguiente programa es correcto:
2
e) {P } x := x ∗ x ∗ x − 2 ∗ x + 4 {x − 5x + 2x > 0}
f) {P } x := x + 1 {x = x + 1}
g) {P } x := E {x = E}
h) {P } x := x mod 2 {x = x mod 2}
i) {P } x, y := x + 1, y − 1 {x + y > 0}
{x > 5}
if
x ≤ y then skip else x, y := y, x
{x ≤ y}
13.13 Usar el método (13.2) para probar que el siguiente programa es correcto:
j) {P } x, y := y + 1, x − 1 {x > 0}
{x = X}
if
x < 0 then x := −x else skip
{x = abs.X}
k) {P } a := a ≡ b {a}
l) {P } a := a ⇒ b {a ∨ b}
13.8 Calcular la precondición más débil P en cada caso:
a) {P } x := x + 1; x := x + 1 {x > 0}.
b) {P } x := x ∗ x; x := x + 1 {x > 0}.
c) {P } x := x 6≡ y; y := x 6≡ y; x := x 6≡ y {(x ≡ X) ∧ (y ≡ Y)}.
d) {P } a := a ≡ b; b := a ≡ b; a := a ≡ b {(a ≡ A) ∧ (b ≡ B)}.
e) {P } x := x + y; y := x − y; x := x − y {x = X ∧ y = Y}.
f) {P } x := y; y := x {x = X ∧ y = Y}.
13.14 Usar el método (13.2) para probar que el siguiente programa es correcto:
{y > 0 ∧ z × xy = X}
if
odd.y then z, y := z × x, y − 1 else x, y := x × x, y/2
{y ≥ 0 ∧ z × xy = X}
13.15 Probar:
g) {P } x := x + 1; skip {x2 − 1 = 0}.
h) {P } skip; x := x + 1 {x2 − 1 = 0}.
13.9 Calcular la expresión que haga válida la tripleta en cada caso
a) {A = q ∗ B + r} q := E; r := r − B {A = q ∗ B + r}.
b) {true} y := E; x := x div 2 {2 ∗ x = y}.
c) {x ∗ y + p ∗ q = N} x := x − p; q := E {x ∗ y + p ∗ q = N}.
13.10 Supongamos que el número de manzanas que tienen Juan y María (m y j respectivamente) están relacionadas con la siguiente fórmula (donde C es una constante):
P :C=m+2×j
Encontrar una solución de e en {P ∧ even.m} m, j := m ÷ 2, e {P }.
13.11 Recordemos que los números de Fibonacci F.i están dados por F. 0 = 0, F. 1 = 1 y
F.n = F.(n − 1) + F.(n − 2) para n ≥ 2. El siguiente predicado define las variables n, a
y b:
P : n > 0 ∧ a = F.n ∧ b = F.(n − 1)
Encontrar una solución para e y f en {P } n, a, b := n + 1, e, f {P }.
{true}
if
x ≥ 1 → x := x + 1
[]
x ≤ 1 → x := x − 1
a)
fi
{x 6= 1}
{true}
if
x≥y →
skip
[]
x ≤ y → x, y := y, x
b)
fi
{x ≥ y}
|[var a, b : bool
{true}
if
¬a ∨ b → a := ¬a
c)
[]
a ∨ ¬b → b := ¬b
fi
{a ∨ b}
13.16 Encontrar el predicado P más débil que haga correcto el siguiente programa:
237
238
13. L A P ROGRAMACIÓN I MPERATIVA
|[var x : Int
{P }
x := x + 1
if
x > 0 → x := x − 1
[]
x < 0 → x := x + 2
[]
x=1 →
skip
fi
{x ≥ 1}
13.17 Probar que
{P }
if B0 → S0 ; S
[] B1 → S1 ; S
fi
{Q}
equivale a
{P }
if B0 → S0
[] B1 → S1
fi
S
{Q}
Descargar