Instrucción Perform
Creo que por méritos propios esta instrucción se merece un apartado para ella sola. Es una instrucción que nos permite tranferir el control a otro u otros procedimientos o bien realizar una serie de sentencias dentro de ella misma mientras se cumplan las condiciones que le hayamos indicado. Es la instrucción mas importante tanto por su variedad de formatos, como por el número de veces que se suele usar dentro de un programa, además nos puede hacer mucho mas sencillo el realizar una programación estructurada.

Cuando decimos que un programa está estructurado, no cabe duda que es debido al uso de ésta instrucción. Pero creo que lo importante no es tanto hablar de ella, sino comenzar a explicarla.

Empecemos a ver sus formatos desde el mas simple al mas complicado: Siempre debéis de tener en cuenta que los ejemplos son solo para aclarar la explicación, nunca los toméis como programas completos y con una lógica aplastante.


Formato 1: Con este formato transferimos el control del programa a un párrafo, cuando éste termine vuelve el control a la instrucción que sigue al PERFORM .

PERFORM nombre_parrafo

...
WORKING-STORAGE SECTION.
01  NOMBRE PIC X(30).
01  OP PIC X.
PROCEDURE DIVISION.
INICIO.
       DISPLAY 'PROGRAMA DE SALUDO' LINE 1 ERASE.
       PERFORM PIDENOMBRE.
       DISPLAY 'HOLA ' LINE 10.
       DISPLAY NOMBRE LINE 10 COL 6.
       ACCEPT OP LINE 20.
       STOP RUN.
PIDENOMBRE.
       DISPLAY 'INTRODUZCA EL NOMBRE ..' LINE 20.
       ACCEPT NOMBRE LINE 20 COL 30 PROMPT.
       DISPLAY SPACES LINE 20 SIZE 70.
...

Nota:
Simplemente hemos hecho que el programa salte a un párrafo que lo ejecute y que vuelva el control a la secuencia.


Formato 2: Una extensión del anterior es indicarle que ejecute mas de un párrafo, especificando el inicio y el fin.

PERFORM nombre_parrafo THRU nombre_parrafo

...
WORKING-STORAGE SECTION.
01  NOMBRE PIC X(12).
01  APELLIDO PIC X(12).
01  OP PIC X.
PROCEDURE DIVISION.
INICIO.
       DISPLAY 'PROGRAMA DE SALUDO' LINE 1 ERASE.
       PERFORM PIDENOMBRE THRU PIDEAPELLIDO.
       DISPLAY 'HOLA ' LINE 10.
       DISPLAY NOMBRE LINE 10 COL 6.
       DISPLAY APELLIDO LINE 10 COL 20.
       ACCEPT OP LINE 20.
       STOP RUN.
PIDENOMBRE.
       DISPLAY 'INTRODUZCA EL NOMBRE ..' LINE 20.
       ACCEPT NOMBRE LINE 20 COL 30 PROMPT.
PIDEAPELLIDO.
       DISPLAY 'INTRODUZCA EL APELLIDO ..' LINE 21.
       ACCEPT APELLIDO LINE 21 COL 30 PROMPT.
       DISPLAY SPACES LINE 20 SIZE 70.
       DISPLAY SPACES LINE 21 SIZE 70. 
...

Nota:
En este caso PIDENOMBRE y PIDEAPELLIDO van seguidos, pero al poner el THRU lo que conseguimos es que el control no se devuelva hasta llegar al párrafo indicado después del THRU. Es decir que entre PIDENOMBRE y PIDEAPELLIDO podrá haber cinco párrados mas y todos ellos se hubieran ejecutado.
Formato 3: Seguimos ampliando las capacidades, ahora conseguimos que el PERFORM se realice tantas veces como se indique en el número o variable que va delante de TIMES.

PERFORM nombre_parrafo THRU nombre_parrafo número-variable TIMES

...
WORKING-STORAGE SECTION.
01  NOMBRE   PIC X(12).
01  APELLIDO PIC X(12).
01  OP           PIC X.
01  LI            PIC 99 VALUE 10.
PROCEDURE DIVISION.
INICIO.
       DISPLAY 'PROGRAMA DE SALUDO' LINE 1 ERASE.
       PERFORM PIDENOMBRE THRU PIDEAPELLIDO.
       PERFORM SALUDAR 3 TIMES.
       STOP RUN.
PIDENOMBRE.
       DISPLAY 'INTRODUZCA EL NOMBRE ..' LINE 20.
       ACCEPT NOMBRE LINE 20 COL 30 PROMPT.
PIDEAPELLIDO.
       DISPLAY 'INTRODUZCA EL APELLIDO ..' LINE 21.
       ACCEPT APELLIDO LINE 21 COL 30 PROMPT.
       DISPLAY SPACES LINE 20 SIZE 70.
       DISPLAY SPACES LINE 21 SIZE 70.
SALUDAR.
       ADD 1 TO LI.
       DISPLAY 'HOLA ' LINE LI.
       DISPLAY NOMBRE LINE LI COL 6.
       DISPLAY APELLIDO LINE LI COL 20.
       ACCEPT OP LINE 20.
...

Nota:
Ahora hemos conseguido que el saludo nos lo muestre 3 veces. Por supuesto THRU y TIMES pueden ir perfectamente juntos. Ya os he dicho al principio que los ejemplos pueden no ser muy lógicos, pero si hacen la función de explicación.


Formato 4:
Igual que el antrior formato solo que el número de veces que se ejecute dependerá de una condición y no de un número fijo.

PERFORM nombre_parrafo THRU nombre_parrafoUNTIL condición

...
WORKING-STORAGE SECTION.
01  NOMBRE   PIC X(12).
01  APELLIDO PIC X(12).
01  OP           PIC X.
01  LI            PIC 99 VALUE 10.
PROCEDURE DIVISION.
INICIO.
       DISPLAY 'PROGRAMA DE SALUDO' LINE 1 ERASE.
       PERFORM PIDENOMBRE THRU PIDEAPELLIDO.
       PERFORM SALUDAR UNTIL LI = 15.
       STOP RUN.
PIDENOMBRE.
       DISPLAY 'INTRODUZCA EL NOMBRE ..' LINE 20.
       ACCEPT NOMBRE LINE 20 COL 30 PROMPT.
PIDEAPELLIDO.
       DISPLAY 'INTRODUZCA EL APELLIDO ..' LINE 21.
       ACCEPT APELLIDO LINE 21 COL 30 PROMPT.
       DISPLAY SPACES LINE 20 SIZE 70.
       DISPLAY SPACES LINE 21 SIZE 70.
SALUDAR.
       ADD 1 TO LI.
       DISPLAY 'HOLA ' LINE LI.
       DISPLAY NOMBRE LINE LI COL 6.
       DISPLAY APELLIDO LINE LI COL 20.
       ACCEPT OP LINE 20.
...

Nota:
En este caso el compilador comprueba antes de iniciar el PERFORM que la condición no se cumple para ejecutarla, en el momento que se cumpla salta a la siguiente instrucción. En el ejemplo ejecutará el PERFORM hasta que la variable LI alcance el valor 15.
PRIMERA CONCLUSION

En los dos primeros formatos, como podréis comprobar a menos que sean instrucciones que se vayan a llamar desde nuestro programa en mas de una ocasión, éstas podían haber ido en el lugar del PERFORM, es decir las instrucciones una detrás de otra y evitar el PERFORM.

Hasta ahora se ha explicado una manera de utilizar la sentencia PERFORM en la que el control pasa a otro lugar del programa. Como véis el STOP RUN está antes de los párrafos que han sido llamados con los PERFORM lo cual indica que éstos no se ejecutarán sino es precisamente por los PERFORM.
Si bien con esto conseguimos una estructuración para nuestro programa la instrucción PERFORM nos permite un mayor grado de estructura incluyendo las sentencias de los párrafos llamados dentro de la propia sentencia PERFORM.

Para ello hay que tener en cuenta que la utilización del punto en las instrucciones daría lugar al fin de la instrucción y nos daría errores, por ello, para finalizar la instrucción nos basamos en END-PERFORM. Veamos un ejemplo de ello con los dos últimos formatos explicados anteriormente.

PERFORM número-variable TIMES
       sentencias
END-PERFORM

PERFORM UNTIL condición
     sentencias
END-PERFORM

...
WORKING-STORAGE SECTION.
01  NOMBRE   PIC X(12).
01  APELLIDO PIC X(12).
01  OP           PIC X.
01  LI            PIC 99 VALUE 10.
PROCEDURE DIVISION.
INICIO.
       DISPLAY 'PROGRAMA DE SALUDO' LINE 1 ERASE.
       PERFORM PIDENOMBRE THRU PIDEAPELLIDO.
       PERFORM 3 TIMES
            ADD 1 TO LI
            DISPLAY 'HOLA ' LINE LI
            DISPLAY NOMBRE LINE LI COL 6
            DISPLAY APELLIDO LINE LI COL 20
            ACCEPT OP LINE 20
       END-PERFORM
       STOP RUN.
PIDENOMBRE.
       DISPLAY 'INTRODUZCA EL NOMBRE ..' LINE 20.
       ACCEPT NOMBRE LINE 20 COL 30 PROMPT.
PIDEAPELLIDO.
       DISPLAY 'INTRODUZCA EL APELLIDO ..' LINE 21.
       ACCEPT APELLIDO LINE 21 COL 30 PROMPT.
       DISPLAY SPACES LINE 20 SIZE 70.
       DISPLAY SPACES LINE 21 SIZE 70.
...
 
...
WORKING-STORAGE SECTION.
01  NOMBRE   PIC X(12).
01  APELLIDO PIC X(12).
01  OP           PIC X.
01  LI            PIC 99 VALUE 10.
PROCEDURE DIVISION.
INICIO.
       DISPLAY 'PROGRAMA DE SALUDO' LINE 1 ERASE.
       PERFORM PIDENOMBRE THRU PIDEAPELLIDO.
       PERFORM UNTIL LI = 15
            ADD 1 TO LI
            DISPLAY 'HOLA ' LINE LI
            DISPLAY NOMBRE LINE LI COL 6
            DISPLAY APELLIDO LINE LI COL 20
            ACCEPT OP LINE 20
       END-PERFORM
       STOP RUN.
PIDENOMBRE.
       DISPLAY 'INTRODUZCA EL NOMBRE ..' LINE 20.
       ACCEPT NOMBRE LINE 20 COL 30 PROMPT.
PIDEAPELLIDO.
       DISPLAY 'INTRODUZCA EL APELLIDO ..' LINE 21.
       ACCEPT APELLIDO LINE 21 COL 30 PROMPT.
       DISPLAY SPACES LINE 20 SIZE 70.
       DISPLAY SPACES LINE 21 SIZE 70.
...

Nota:
Esta es una manera mas lógica de utilizar el PERFORM en estos formatos, como veís se consigue una visión muy clara de lo que estamos haciendo y no hay saltos de secuencia innecesarios. Por supuesto el COBOL permite cualquier modalidad de uso y siempre funcionaría igual. Como veis no puede haber puntos entre el PERFORM y el END-PERFORM puesto que eso haría terminar con error la sentencia.
Formato 5: En esta ocasión utilizaremos el PERFORM basándonos en un valor inicial el cual podremos y aumentar o disminuir y terminar cuando se cumpla una condición. No se si me he liado un poco con la explicación pero lo que conseguimos es ahorrarnos varias instrucciones y resumirlas en una sola, para que lo comprendáis veamos su formato y un ejemplo. Como he explicado anteriormente podemos hacer que el PERFORM invoque a uno o varios párrafos o bien incluir el conjunto de sentencias entre PERFORM y END-PERFORM.
En el ejemplo veremos este último caso, porque creo que es mas aconsejable.

PERFORM  VARYING variable FROM número, variable BY número, valor  UNTIL  condición
     sentencias
END-PERFORM

...
WORKING-STORAGE SECTION.
01  NOMBRE   PIC X(12).
01  APELLIDO PIC X(12).
01  OP           PIC X.
01  LI            PIC 99.
PROCEDURE DIVISION.
INICIO.
       DISPLAY 'PROGRAMA DE SALUDO' LINE 1 ERASE.
       PERFORM PIDENOMBRE THRU PIDEAPELLIDO.
       PERFORM VARYING LI FROM 10 BY 1 UNTIL LI = 15
            DISPLAY 'HOLA ' LINE LI
            DISPLAY NOMBRE LINE LI COL 6
            DISPLAY APELLIDO LINE LI COL 20
            ACCEPT OP LINE 20
       END-PERFORM
       STOP RUN.
PIDENOMBRE.
       DISPLAY 'INTRODUZCA EL NOMBRE ..' LINE 20.
       ACCEPT NOMBRE LINE 20 COL 30 PROMPT.
PIDEAPELLIDO.
       DISPLAY 'INTRODUZCA EL APELLIDO ..' LINE 21.
       ACCEPT APELLIDO LINE 21 COL 30 PROMPT.
       DISPLAY SPACES LINE 20 SIZE 70.
       DISPLAY SPACES LINE 21 SIZE 70.
...


La instrucción realiza los siguientes pasos:
Inicializa la variable LI con el valor que sigue al FROM, es decir 10, a continuación va incrementado el valor de LI en 1 que es lo que va después del BY y ejecuta las sentencias que van a continuación hasta que se cumple la condición de LI = 15.
Es decir las sentencias se ejecutarían para el valor 10, 11, 12, 13 y 14.

La instrucción PERFORM se puede anidar tantas veces como se quiera, teniendo en cuenta que el PERFORM contenido debe de ejecutarse completamente interno o completamente externo al que lo contiene.

Si necesitamos que por alguna circunstancia finalice la ejecución de un PERFORM aún cuando la secuencia no corresponde, podemos utilizar la sentencia EXIT, que precisamente lo que propicia es eso, que se interrumpa la sentencia PERFORM.

Para finalizar vamos a ver un ejemplo con varios PERFORM anidados:

...
WORKING-STORAGE SECTION.
01  NOMBRE   PIC X(12).
01  APELLIDO PIC X(12).
01  SALUDO   PIC X(30).
01  OP           PIC X.
01  LI            PIC 99.
01  CONTA1   PIC 99.
01  CONTA2   PIC 9(6).
PROCEDURE DIVISION.
INICIO.
       DISPLAY 'PROGRAMA DE SALUDO' LINE 1 ERASE
       PERFORM PIDENOMBRE THRU PIDEAPELLIDO
       STRING 'HOLA ' DELIMITED BY SIZE NOMBRE DELIMITED BY ' '
       ' ' DELIMITED BY SIZE APELLIDO DELIMITED BY ' ' INTO SALUDO
       PERFORM VARYING LI FROM 10 BY 1 UNTIL LI = 15
            PERFORM VARYING CONTA1 FROM 1 BY 1 UNTIL CONTA1 > 30
                 DISPLAY SALUDO(CONTA1:1) LINE LI COL CONTA1 LOW
                 PERFORM VARYING CONTA2 FROM 1 BY 1 UNTIL CONTA2 > 400000
                    MOVE ' ' TO OP
                 END-PERFORM
            END-PERFORM
       END-PERFORM
       DISPLAY 'FINALIZADO' LINE 22
       ACCEPT OP
       STOP RUN.
PIDENOMBRE.
       DISPLAY 'INTRODUZCA EL NOMBRE ..' LINE 20
       ACCEPT NOMBRE LINE 20 COL 30 PROMPT.
PIDEAPELLIDO.
       DISPLAY 'INTRODUZCA EL APELLIDO ..' LINE 21
       ACCEPT APELLIDO LINE 21 COL 30 PROMPT
       DISPLAY SPACES LINE 20 SIZE 70
       DISPLAY SPACES LINE 21 SIZE 70.
...

Nota:
En el ejemplo como véis, después de aceptar el nombre y el apellido construimos una frase y la guardamos en la variablen SALUDO utilizando el comando STRING. A continuación y para mantener los mismos ejemplos de toda la sección hacemos que aparezca en pantalla el SALUDO 5 veces, (dependiendo del valor de LI, que como dijimos antes sería para 10, 11, 12, 13 y 14).

Pero ahora hemos intercalado dos PERFORM dentro de éste, el primero para que nos muestre el mensaje letra a letra y el segundo para hacer de retardo y así conseguir un efecto como si escribiéramos el SALUDO con una máquina de escribir.

Como pódeis observar el único punto de toda la secuencia se haya en el STOP RUN. El hecho de mantener los márgenes izquierdos, es para dar mas claridad a la programación y conseguir que ésta sea lo mas estructurada posible.

AUN HAY MAS

Pues sí, podemos desarrollar aún mas la instrucción incluyendo dos nuevos elementos.

EJECUTANDO ANTES O DESPUES

En primer lugar, por defecto el contenido del PERFORM se ejecuta después de hacer la comprobación de la condición que precede a UNTIL , pero podemos hacer que ésta se realice antes.

PERFORM  WITH TEST  [AFTER - BEFORE ] VARYING variable FROM número, variableBY número, valorUNTIL condición
     sentencias
END-PERFORM

Con la opción TEST BEFORE, que es la opción por defecto se comprueba primero la condición y si se cumple se ejcuta el resto, con lo cual es posible que las sentencias no se ejecuten ninguna vez, si al comenzar ya está rota la condición.

Con la opción TEST AFTER, se ejecutan las sentencias antes de comprobar la condición, con lo cual las sentencias se van a ejecutar al menos en una ocasión, incluso aunque entremos en el PERFORM con la condición rota.

AUMENTANDO LAS CONDICIONES

Efectivamente además de la primera condición podemos incrementar el número de condiciones para que se cumplan los requisitos y así ejecutar las sentencias. Esto lo conseguimos incluyendo mas cláusulas precedidas de AFTER.

PERFORM WITH TEST [AFTER - BEFORE ] VARYING variable FROM número, variable BY número, valor UNTIL condición
AFTER variable FROM número, variable BY número, valor UNTIL condición
     sentencias
AFTER ......
END-PERFORM

Al aplicar este formato cuando se cumpla la primera condición pasará el control al AFTER y comprobará de nuevo la condición que precede al siguiente UNTIL según los valores especificados en la línea que contiene AFTER. Además podemos ir incrementando el número de condiciones a nuestro antojo, consiguiendo de éste modo hacer unos anidamientos y un desarrollo mas completo de la sentencia, obteniendo por consiguiente unos niveles de perfeccionamiento muy altos.

RESUMIENDO

Una mayor utilización de PERFORM sin duda traerá un uso menor de la declaración GO y con ello conseguimos una programación estructurada. Las ventajas de la programación estructurada las quiero explicar en un apartado dentro de ésta misma sección de Manuales, una vez explique las sentenicas GO e IF,
que serán la siguiente aportación al manual.

En esa nueva sección se verá un caso mas práctico y real de utilización de PERFORM con uso de lecturas de ficheros y demás experiencias cotidianas en el mundo de la programación en el ámbito de la gestión.



Este artículo proviene de Cobol en español
http://www.escobol.com

La dirección de esta noticia es:
http://www.escobol.com/modules.php?name=Sections&op=viewarticle&artid=20