Anda di halaman 1dari 14

ANÁLISIS Y VERIFICACIÓN DE ALGORITMOS

UNIDAD UNO - SEMANA DOS


SINTAXIS Y SEMÁNTICA OPERACIONAL DE GCL
AUTOR: ALEJANDRO SOTELO ARÉVALO

TABLA DE CONTENIDO

1. EL LENGUAJE DE COMANDOS GUARDADOS (GCL) 2


2. SINTAXIS Y SEMÁNTICA OPERACIONAL DE GCL 3
2.1. SKIP (INSTRUCCIÓN VACÍA) 3
2.2. ABORT (TERMINACIÓN ANORMAL) 4
2.3. ASIGNACIÓN 5
2.5. CONCATENACIÓN (SECUENCIAS DE PROGRAMAS) 6
2.6. SELECCIÓN (INSTRUCCIONES CONDICIONALES) 7
2.7. REPETICIÓN (CICLOS) 11
EN RESUMEN 13
PARA TENER EN CUENTA 14

ANÁLISIS Y VERIFICACIÓN DE ALGORITMOS


ALEJANDRO SOTELO ARÉVALO
1
1. EL LENGUAJE DE COMANDOS GUARDADOS (GCL)

Hasta el momento nos hemos preocupado de especificar la precondición y poscondición de


los programas y de describir operacionalmente cuando un programa es correcto con
respecto a su especificación, pero no de escribir programas. Existen muchísimos lenguajes
diseñados para programar: Fortran, ALGOL, LISP, COBOL, BASIC, Pascal, Prolog, C, Scheme,
C++, Perl, Haskell, Python, Visual Basic, Ruby, PHP, Java, C#, etc., cada uno con sus ventajas y
desventajas, y con sus seguidores y contradictores.

La mayoría de los lenguajes de programación a los que estamos acostumbrados siguen el


paradigma de la programación imperativa, en la que los programas son vistos como
secuencias de instrucciones que cambian el estado de las variables, describiendo paso o paso
cómo se realiza una determinada tarea. Sin embargo, existen otros lenguajes de
programación, que siguen el paradigma de la programación declarativa, en la que los
programas son escritos mediante la declaración de reglas lógicas que describen el problema
sin exhibir explícitamente las instrucciones que componen el algoritmo que lo soluciona.

Gráfica 1: Algunos lenguajes de programación creados a lo largo de la historia, con su fecha de aparición.
Scheme, Perl, Python y Ruby son multiparadigma, siguiendo tanto el paradigma imperativo como el declarativo.

Para expresar programas usaremos el Lenguaje de Comandos Guardados (denominado GCL


por sus siglas en inglés: Guarded Command Language), inventado por Edsger Dijkstra. GCL es
un lenguaje especialmente diseñado para facilitar el estudio de los algoritmos, que consta de
un conjunto reducido de instrucciones lo suficientemente expresivas como para permitir
describir comandos como condicionales y ciclos, y lo suficientemente simples como para
poder estudiar los algoritmos sin tener que preocuparnos por instrucciones complejas
difíciles de comprender y de analizar.

ANÁLISIS Y VERIFICACIÓN DE ALGORITMOS


ALEJANDRO SOTELO ARÉVALO
2
2. SINTAXIS Y SEMÁNTICA OPERACIONAL DE GCL

La sintaxis de GCL enumera las reglas que definen como escribir programas en el lenguaje,
brindando los siguientes tipos de instrucción: 1. skip (instrucción vacía), 2. abort
(terminación anormal), 3. Asignación, 4. Concatenación (secuencias de programas), 5.
Selección (instrucciones condicionales) y 6. Repetición (ciclos).

La sintaxis por sí sola no es suficiente para definir un lenguaje. Por ejemplo, una persona
podría hablar perfectamente en chino sin entender que está diciendo, o cantar una canción
en inglés sin comprender la letra. Por lo tanto, además de la sintaxis, es necesario definir la
semántica del lenguaje, que nos provee el significado.

Así pues, los programas en GCL pueden interpretarse bajo una semántica o significado que
nos indique cómo entenderlos. Existen dos maneras importantes para dotar de significado a
los programas en GCL:
Semántica Operacional: el significado de los programas se establece describiendo su
operación en términos de cómo se ejecutan en una máquina instrucción tras instrucción,
transformando el estado de las variables.
Semántica Axiomática: el significado de los programas se establece mediante axiomas
formales que describen bajo qué condiciones éstos son correctos con respecto a su
especificación.

Se estudiará con detalle cada tipo de instrucción del lenguaje de programación GCL
definiendo su sintaxis y su semántica operacional, con la ayuda de diagramas de flujo. Más
adelante definiremos la semántica axiomática.

2.1. SKIP (INSTRUCCIÓN VACÍA)


Código 2: Instrucción vacía.
skip

El comando más sencillo en GCL es la instrucción vacía skip, que no realiza ningún cambio
sobre el estado de las variables (coloquialmente, la instrucción skip no hace nada). Su
significado operacional se establece afirmando que, después de ejecutarse un skip, el estado
de las variables permanece igual a como estaba antes de ejecutarse la instrucción.
Formalmente, para todo estado que satisface la precondición , después de ejecutarse el
comando skip, se termina en el mismo estado .

Si , entonces es una Tripla de Hoare válida porque cuando se comienza la


ejecución en un estado que satisface la precondición , después de ejecutarse el comando

ANÁLISIS Y VERIFICACIÓN DE ALGORITMOS


ALEJANDRO SOTELO ARÉVALO
3
skip (que no hace nada), se termina en un estado que satisface (porque al final se cumple
, y como , entonces también se cumple ).

En los diagramas de flujo, representaremos al comando skip con una flecha que dé
continuación al flujo de ejecución sin tener ningún efecto sobre el estado de las variables:

Gráfica 3: Diagrama de flujo correspondiente a la instrucción skip.

En Java y C++ hay muchas posibilidades para simular una instrucción vacía, que van más allá
de simplemente no escribir código.

Tabla 4: Paralelo entre código en Java/C++ y código en GCL.


Código en Java/C++ Código en GCL equivalente
{
skip
}
if (false) {
skip
}
;; skip

2.2. ABORT (TERMINACIÓN ANORMAL)


Código 5: Terminación anormal.
abort

Para terminar abruptamente una ejecución, se cuenta en GCL con la instrucción abort, que
termina anormalmente el flujo de operaciones de un programa. Formalmente, para todo
estado que satisface la precondición , después de ejecutarse el comando abort, la
ejecución termina anormalmente.

Si , entonces es una Tripla de Hoare válida gracias a la ley de


precondición vacía, que afirma que . Por otro parte, si ,
entonces no es una Tripla de Hoare válida, porque el comando abortaría la
ejecución al comenzar en cualquier estado que satisfaga la precondición . Por lo tanto,
es una Tripla de Hoare válida si y sólo si .

En los diagramas de flujo, representaremos al comando abort con un nodo que termine
abruptamente el flujo de ejecución del programa:

Gráfica 6: Diagrama de flujo correspondiente a la instrucción abort.

ANÁLISIS Y VERIFICACIÓN DE ALGORITMOS


ALEJANDRO SOTELO ARÉVALO
4
En Java y C++ se puede simular una terminación anormal lanzando una excepción o
ejecutando una operación no permitida como una división por cero o un acceso por fuera de
los límites de un arreglo.

2.3. ASIGNACIÓN
Código 7: Asignación.
x1,x2,...,xn:=E1,E2,...,En

Para transformar el estado de las variables se tiene la asignación ,


donde es una lista de variables distintas y es una lista de
expresiones cuyos tipos coinciden con el de sus respectivas variables (el tipo de la expresión
debe ser el mismo tipo que el de la variable para todo desde hasta ). Esta
instrucción toma el nombre de asignación simultánea cuando en la asignación se está
modificando el valor de más de una variable.

Operacionalmente, la asignación se interpreta de la siguiente


manera:
1. Se evalúa el valor de cada una de las expresiones en el estado. En caso de que
alguna expresión no esté definida en el estado en la que se evalúa, se aborta la ejecución.
2. Se asigna simultáneamente a las variables el valor de las expresiones
, respectivamente. En otras palabras, a la variable se le asigna el valor de la
expresión , a la variable se le asigna el valor de la expresión , …, y a la variable se
le asigna el valor de la expresión , donde todas las asignaciones se ejecutan
simultáneamente.

En particular, la instrucción le asigna a la variable el valor de la expresión .

Durante la ejecución de un programa en GCL, cualquier transformación en el estado de las


variables debe ser causada por alguna asignación. Después de ejecutarse una asignación
, se cumple para todo desde hasta que la variable termina
con el valor que tenía la expresión antes de ejecutarse la instrucción.

En los diagramas de flujo, representaremos a la asignación con


un nodo capaz de actuar de acuerdo a la semántica operacional descrita, y que luego dé
continuación al flujo de ejecución:

Gráfica 8: Diagrama de flujo correspondiente a la asignación.

ANÁLISIS Y VERIFICACIÓN DE ALGORITMOS


ALEJANDRO SOTELO ARÉVALO
5
Tabla 9: Paralelo entre código en Java/C++ y código en GCL.
Código en Java/C++ Código en GCL equivalente
a=5;
b=7;
a,b,c,d,:=5,7,4,0
c=4;
d=0;
a=5; a,b:=5,8
b=a+3; ¿Por qué sería un error la traducción a,b:=5,a+3?
b=a+3;
b,a:=a+3,5
a=5;
a=b;
a,b:=b,5
b=5;
b=5;
a,b:=5,5
a=b;
a++; a:=a+1
a++;
b--;
c+=2;
a,b,c,d,e,f:=a+1,b-1,c+2,d-3,e*4,f/8
d-=3;
e*=4;
f/=8;

Es posible traducir asignaciones en GCL a lenguajes como Java y C++, teniendo especial
cuidado con las asignaciones simultáneas, que pueden requerir de la creación de variables
auxiliares o temporales durante la traducción.

Tabla 10: Paralelo entre código en GCL y código en Java/C++.


Código en GCL Código en Java/C++ equivalente
temp=a;
a,b:=3,a a=3;
b=temp;
b=a;
a=3;
a,b:=3,a
(alterando el orden de las asignaciones es posible prescindir
del uso de variables temporales)
temp=a;
a,b:=b,a a=b;
b=temp;
tempA=a;
tempB=b;
tempC=c;
a,b,c:=b+a+c,b*a-c+3,a-b+c
a=tempB+tempA+tempC;
b=tempB*tempA-tempC+3;
c=tempA-tempB+tempC;

2.5. CONCATENACIÓN (SECUENCIAS DE PROGRAMAS)


Código 11: Concatenación de programas.
S1;S2

ANÁLISIS Y VERIFICACIÓN DE ALGORITMOS


ALEJANDRO SOTELO ARÉVALO
6
Para ejecutar dos programas uno tras de otro como una secuencia de dos acciones
consecutivas, está la concatenación (también conocida como secuenciación) donde
tanto como son programas en GCL.

En una secuencia de programas , el punto y coma ( ) es utilizado como un operador


binario que recibe dos programas como parámetro y que entrega como resultado un
programa que combina ambos. Bajo ninguna circunstancia el punto y coma ( ) en GCL actúa
como separador de instrucciones o como fin de instrucción tal y como se hace en lenguajes
como C, C++ y Java.

Operacionalmente, la concatenación se interpreta de la siguiente manera: primero se


ejecuta el programa , y luego se ejecuta el programa . Observe que el operador de
concatenación ( ) no es conmutativo porque no es lo mismo que en general: en
el primer caso, se ejecuta primero y luego , mientras que en el segundo caso se ejecuta
primero y luego . Lo que sí se cumple es que la concatenación es asociativa, porque
tiene el mismo significado operacional que : primero se ejecuta ,
luego , y finalmente .

Aprovechando que la concatenación es asociativa, se pueden eliminar paréntesis


innecesarios en secuencias de varios programas, permitiendo escribir como
abreviación de . Operacionalmente, primero ejecuta
, luego ejecuta , después ejecuta , …, y finalmente ejecuta .

En los diagramas de flujo, representaremos la concatenación de la siguiente manera:

Gráfica 12: Diagrama de flujo correspondiente a la concatenación.

2.6. SELECCIÓN (INSTRUCCIONES CONDICIONALES)


Código 13: Instrucción de selección.
if B1  S1
[] B2  S2
...
[] Bn  Sn
fi

Para soportar comandos condicionales se tiene la instrucción de selección IF, donde:


 son expresiones booleanas llamadas guardas del condicional.

ANÁLISIS Y VERIFICACIÓN DE ALGORITMOS


ALEJANDRO SOTELO ARÉVALO
7
 son programas llamados comandos del condicional.
Cada expresión de la forma es un comando guardado del condicional.
 es el número de comandos guardados del condicional ( ).

Se define la disyunción de las guardas (abreviada ) de la siguiente manera:

La disyunción de las guardas es verdadera en un estado si existe alguna guarda que sea
verdadera en tal estado, y es falsa en un estado si todas las guardas son falsas en el estado.
Además, note que gracias al teorema de De Morgan
Generalizado.

Se dice que un algoritmo es determinístico si y sólo si para todo estado que cumpla la
precondición, cada vez que se ejecute el programa comenzando en tal estado, se sigue el
mismo flujo de operaciones y se llega al mismo resultado. Por otro lado, un algoritmo es no
determinístico si y sólo si existe un estado que cumpla la precondición, tal que sea posible
que hayan dos ejecuciones que sigan distintos flujos de operaciones y/o lleguen a resultados
distintos.

Operacionalmente, el condicional se interpreta de la


siguiente manera:
1. Se evalúan simultáneamente todas las guardas en el estado. En caso de que
alguna guarda no esté definida en el estado en la que se evalúa, se aborta la ejecución.
2. Si ninguna de las guardas es verdadera en el estado, se aborta abruptamente la ejecución
del condicional.
3. De lo contrario, si por lo menos una de las guardas es verdadera en el estado:
3.1. Se escoge no determinísticamente alguna de las guardas que se cumpla en el estado.
3.2. Se ejecuta el comando correspondiente a la guarda seleccionada.

En los diagramas de flujo, representaremos el condicional IF así:

Gráfica 14: Diagrama de flujo correspondiente a la instrucción condicional.

ANÁLISIS Y VERIFICACIÓN DE ALGORITMOS


ALEJANDRO SOTELO ARÉVALO
8
Es importante recalcar los siguientes hechos relacionados con la semántica operacional de
los condicionales:
Si ninguna guarda se cumple (si se satisface), el condicional inevitablemente aborta su
ejecución.
Si alguna guarda se cumple (si se satisface):
o Si exactamente una guarda se cumple, su comando correspondiente es ejecutado.
o Si más de una guarda se cumple, se escoge arbitrariamente cualquiera de las guardas que
se cumplen y se ejecuta su comando correspondiente. Este comportamiento es no
determinístico porque no se puede saber de antemano cuál de las guardas va a ser
seleccionada.
En toda situación, exactamente una guarda es escogida y exactamente un comando del
condicional es ejecutado (el que corresponde a la guarda seleccionada).
Es posible que hayan distintos flujos de operaciones comenzando la ejecución en el mismo
estado (debido al no determinismo).
Los comandos guardados de un condicional se pueden reordenar sin afectar la semántica
operacional.

Considere el siguiente programa, que almacena en la variable el máximo entre y :

Código 15: Programa no determinístico en GCL que calcula el máximo entre dos números.
con x: ,y: ;
var r: ;
{Pre Q: true}
if xy  r:=x
[] yx  r:=y
fi
{Pos R: r=xy}

Operacionalmente, el condicional se interpreta de la siguiente manera: si , entonces a la


variable se le asigna el valor de la variable ; pero si , entonces a la variable se le
asigna el valor de la variable . Vea que la variable termina siendo el máximo entre y . El
algoritmo es no determinístico porque cuando , ambas guardas del condicional se
cumplen, razón por la que no se conocería de antemano cuál de los dos comandos se
terminaría ejecutando. Cuando se comienza en un estado en el que , va a quedar con
el mismo valor independientemente del comando que se ejecute (ya sea ó ). Sin
mucho esfuerzo, se puede escribir un programa determinístico que logre el mismo objetivo:

Código 16: Programa determinístico en GCL que calcula el máximo entre dos números.
con x: ,y: ;
var r: ;
{Pre Q: true}
if x>y  r:=x
[] yx  r:=y
fi
{Pos R: r=xy}

ANÁLISIS Y VERIFICACIÓN DE ALGORITMOS


ALEJANDRO SOTELO ARÉVALO
9
Tabla 17: Paralelo entre código en Java/C++ y código en GCL.
Código en Java/C++ Código en GCL equivalente
if (B) { if B  S
S [] ¬B  skip
} fi
if (B) {
S1
if B  S1
}
else { [] ¬B  S2
S2 fi
}
if (B1) {
S1
} if B1  S1
else if (B2) { [] ¬B1  B2  S2
S2 ...
} [] ¬B1  ¬B2  ...  ¬Bn-1  Bn  Sn
... [] ¬B1  ¬B2  ...  ¬Bn-1  ¬Bn  skip
else if (Bn) { Fi
Sn
}
if (B1) {
S1
}
else if (B2) { if B1  S1
S2
[] ¬B1  B2  S2
}
...
...
else if (Bn) { [] ¬B1  ¬B2  ...  ¬Bn-1  Bn  Sn
Sn [] ¬B1  ¬B2  ...  ¬Bn-1  ¬Bn  Sn+1
} fi
else {
Sn+1
}
SWITCH típico:
switch (x) {
case c1:
S1
break;
case c2: if x=c1  S1
S2 [] x=c2  S2
break; ...
... [] x=cn  Sn
case cn: [] xc1  xc2  ...  xcn  Sn+1
Sn fi
break;
default:
Sn+1
break;
}
if B  x:=E
Expresión condicional típica aplicada a una asignación:
x=B?E:F; [] ¬B  x:=F
fi

ANÁLISIS Y VERIFICACIÓN DE ALGORITMOS


ALEJANDRO SOTELO ARÉVALO
10
2.7. REPETICIÓN (CICLOS)
Código 18: Instrucción de repetición.
do B1  S1
[] B2  S2
...
[] Bn  Sn
od

Para soportar comandos iterativos (también llamados ciclos, bucles o loops), se tiene la
instrucción de repetición DO, donde:
 son expresiones booleanas llamadas guardas del ciclo.
 son programas llamados comandos del ciclo.
Cada expresión de la forma es un comando guardado del ciclo.
 es el número de comandos guardados del ciclo ( ).

Para el caso de los ciclos, aplican también los conceptos de determinismo, de no


determinismo y de disyunción de las guardas (
) estudiados en los condicionales.

Operacionalmente, el ciclo se interpreta de la siguiente


manera:
1. Se evalúan simultáneamente todas las guardas en el estado. En caso de que
alguna guarda no esté definida en el estado en la que se evalúa, se aborta la ejecución.
2. Si ninguna de las guardas es verdadera en el estado, se termina la ejecución del ciclo.
3. De lo contrario, si por lo menos una de las guardas es verdadera en el estado:
3.1. Se escoge no determinísticamente alguna de las guardas que se cumpla en el estado.
3.2. Se ejecuta el comando correspondiente a la guarda seleccionada (este proceso se llama
iteración).
3.3. Se regresa al paso 1.

En particular, la instrucción se interpretaría como uno espera: mientras se cumpla


, ejecute , donde es la guarda y es el cuerpo del ciclo.

Durante una ejecución particular, el número de iteraciones de un ciclo es la cantidad de veces


que se ejecuta alguno de sus comandos antes de que se termine su operación.

En los diagramas de flujo, representaremos al ciclo DO así:

ANÁLISIS Y VERIFICACIÓN DE ALGORITMOS


ALEJANDRO SOTELO ARÉVALO
11
Gráfica 19: Diagrama de flujo correspondiente a la instrucción de repetición.

Estudiando la semántica operacional del DO, se observa que el programa


do B1  S1
[] B2  S2
...
[] Bn  Sn
od
hace lo mismo que el programa
do BB  if B1  S1
[] B2  S2
...
[] Bn  Sn
fi
od
porque ambos se describen con el mismo diagrama de flujo.

Dado que todo ciclo con más de un comando guardado puede ser traducido en un ciclo con
exactamente un comando guardado, no perdemos expresividad si todas nuestras
instrucciones repetitivas las escribimos en la forma
do B  S
od

Una de las ventajas de diseñar ciclos con un sólo comando guardado es que la traducción a
lenguajes como Java, C y C++ se facilita.

Código 20: Ciclo de ejemplo que suma los elementos de un arreglo.


con n: , a:array[0..n-1] of ;
var i: ,r: ;
{Pre Q: n0}
r,i:=0,0;
do i<n  r,i:=r+a[i],i+1
od
{Pos R: r=(k|0kn-1:a[k])}

ANÁLISIS Y VERIFICACIÓN DE ALGORITMOS


ALEJANDRO SOTELO ARÉVALO
12
Tabla 21: Paralelo entre código en Java/C++ y código en GCL.
Código en Java/C++ Código en GCL equivalente
WHILE típico donde B es la guarda y S es el cuerpo del ciclo:
while (B) { do B  S
S od
}
FOR típico donde I es la inicialización, B es la guarda, T es el
incremento, y S es el cuerpo del ciclo: I;
for (I; B; T) { do B  S;T
S od
}
DO-WHILE típico donde B es la guarda y S es el cuerpo del
ciclo: S;
do { do B  S
S od
} while (B);
con n: ;
var i: ,r: ;
{Pre Q: n0}
double r=0.0;
r:=0;
for (int i=0; i<n; i++) {
i:=0;
r+=Math.pow(i,2);
} do i<n  r:=r+i2;
i:=i+1
od
{Pos R: r=(k|0kn-1:k2)}

EN RESUMEN
GCL es un lenguaje especialmente diseñado para facilitar el estudio de los algoritmos, que consta de
un conjunto reducido de instrucciones lo suficientemente expresivas como para permitir describir
comandos como condicionales y ciclos, y lo suficientemente simples como para poder estudiar los
algoritmos sin tener que preocuparnos por instrucciones complejas difíciles de comprender y de
analizar.
La sintaxis de GCL enumera las reglas que definen como escribir programas en el lenguaje, brindando
los siguientes tipos de instrucción: 1. skip (instrucción vacía), 2. abort (terminación anormal), 3.
Asignación, 4. Concatenación (secuencias de programas), 5. Selección (instrucciones condicionales) y
6. Repetición (ciclos).
Existen dos maneras importantes para dotar de significado a los programas en GCL:
 Semántica Operacional: el significado de los programas se establece describiendo su operación en
términos de cómo se ejecutan en una máquina instrucción tras instrucción, transformando el estado
de las variables.
 Semántica Axiomática: el significado de los programas se establece mediante axiomas formales que
describen bajo qué condiciones éstos son correctos con respecto a su especificación.
skip (instrucción vacía)
Sintaxis Diagrama de flujo Semántica operacional
No realiza ningún cambio sobre el estado de las
skip
variables.

abort (terminación anormal)

ANÁLISIS Y VERIFICACIÓN DE ALGORITMOS


ALEJANDRO SOTELO ARÉVALO
13
Sintaxis Diagrama de flujo Semántica operacional
Termina anormalmente el flujo de operaciones de
abort
un programa.

Asignación
Sintaxis Diagrama de flujo Semántica operacional
Se asigna simultáneamente a las variables
el valor de las expresiones
, respectivamente. En otras
palabras, a la variable se le asigna el valor de
x1,x2,...,xn:=E1,E2,...,En la expresión , a la variable se le asigna el
valor de la expresión , …, y a la variable se
le asigna el valor de la expresión , donde
todas las asignaciones se ejecutan
simultáneamente.

Concatenación (secuencias de programas)


Sintaxis Diagrama de flujo Semántica operacional

Primero se ejecuta el programa , y luego se


S1;S2
ejecuta el programa .

Selección (instrucciones condicionales)


Sintaxis Diagrama de flujo Semántica operacional
Si ninguna de las guardas es
if B1  S1 verdadera, se aborta abruptamente la ejecución
[] B2  S2 del condicional. De lo contrario, si por lo menos
... una de las guardas es verdadera, se escoge no
[] Bn  Sn determinísticamente alguna de las guardas que se
fi cumpla y se ejecuta el comando correspondiente a
la guarda seleccionada.

Repetición (ciclos)
Sintaxis Diagrama de flujo Semántica operacional

do B1  S1 Mientras alguna de las guardas sea


[] B2  S2 verdadera: se escoge no determinísticamente
... alguna de las guardas que se cumpla y se ejecuta
[] Bn  Sn el comando correspondiente a la guarda
od seleccionada.

PARA TENER EN CUENTA


La siguiente lectura tratará la semántica axiomática de los programas en GCL.

ANÁLISIS Y VERIFICACIÓN DE ALGORITMOS


ALEJANDRO SOTELO ARÉVALO
14

Anda mungkin juga menyukai