miércoles, 19 de abril de 2017

PROCEDURE DIVISION: proceso del programa.

Actualizado: incluimos EVALUATE
La PROCEDURE DIVISION es la parte del programa donde se codifica el proceso en sí. Es decir, aquí escribiremos las sentencias cobol para llevar a cabo la función del programa.

INFORMANDO VARIABLES

En cobol lo que se hace es "mover" la información de una variable a otra utilizando la sentencia MOVE.
A la hora de mover información tendremos que tener en cuenta 2 cosas:
  • Formato(PIC) de ambas variables
  • Longitud de ambas variables

Ejemplo 1.
01 WX-TELEFONO   PIC X(9) VALUE '666111333'.
01 WX-TELEFONO-2 PIC X(9).


Tienen mismo formato y misma longitud, por lo que no habrá problemas. La sentencia sería:
MOVE WX-TELEFONO TO WX-TELEFONO-2

Ahora WX-TELEFONO-2 tendrá el valor '666111333'.

Ejemplo 2.
01 WX-TELEFONO     PIC X(9) VALUE '666111333'.
01 WX-TELEFONO-NUM PIC 9(9).


Ahora tenemos misma longitud pero distinto formato. En este caso la variable alfanumérica sólo tiene números, por lo que no habrá problema.

MOVE WX-TELEFONO TO WX-TELEFONO-NUM
Ahora WX-TELEFONO-NUM tendrá el valor 666111333.

Ejemplo 3.
01 WX-TELEFONO     PIC X(9) VALUE 'AAA111333'.
01 WX-TELEFONO-NUM PIC 9(9).


Ahora tenemos caracteres alfabéticos en la variable alfanumérica, esto no dará error, aunque no tiene mucho sentido hacerlo.

Dependiendo de la instalación, este tipo de movimientos donde pasamos caracteres alfabéticos a campos numéricos darán, o no, un error en ejecución: el MOVE dará un estupendo "casque" al ejecutar con código error S0C7. Lo mismo ocurre si lo movemos a un campo COMP-3 o COMP.
En cualquier caso, NO debemos mover caracteres alfabéticos a un campo numérico, COMP o COMP-3.

Ejemplo 4.
01 WX-TELEFONO     PIC X(9) VALUE '666111333'.
01 WX-TELEFONO-NUM PIC 9(6).


En este caso no coinciden ni las longitudes ni los formatos. Como el campo alfanumérico sólo lleva números no habrá problema, pero al no coincidir las longitudes el campo se "truncará".

MOVE WX-TELEFONO TO WX-TELEFONO-NUM
Ahora el campo WX-TELEFONO-NUM tendrá valor 111333. Hemos perdido los 3 primeros dígitos.

Si fuese al revés:
01 WX-TELEFONO-NUM PIC 9(9) VALUE 666111333.
01 WX-TELEFONO     PIC X(6).

MOVE WX-TELEFONO-NUM TO WX-TELEFONO

El valor del campo WX-TELEFONO sería 666111.

Cuando se mueve a un campo alfanumérico, el movimiento se hace de la posición 1 del campo inicial a la posición 1 del campo alfanumérico.
Cuando se mueve a un campo numérico, el movimiento se hace de la última posición del campo inicial a la última posición del campo numérico.

Ejemplo 5.
01 WX-NUMERO-ALF PIC X(4) VALUE '12 '.
01 WX-NUMERO     PIC 9(4).


Puede darse el caso de que valores numéricos nos vengan con espacios en lugar de ceros a la izquierda.
Una manera de solventar esto es utilizando el COMPUTE FUNCTION NUMVAL:

COMPUTE WX-NUMERO = FUNCTION NUMVAL(WX-NUMERO-ALF)


Ahora la variable WX-NUMERO tendrá el valor 0012. Es una manera de asegurarnos de que no casque el programa porque algún vago no haya escrito los ceros^^
El movimiento en sí no daría error, pero no debe hacerse.

Un ejemplo más claro:
01 WX-NUMERO-ALF PIC X(4) VALUE '12,00  '.
01 WX-NUMERO     PIC 9(4)V9(2).


Con el COMPUTE FUNCTION NUMVAL conseguiríamos que WX-NUMERO tuviese el valor  00120{ (aplantillado con la coma decimal).

Campos COMP-3 y COMP.
Ejemplo 6.
01 WX-SUELDO-COMP3 PIC S9(6) COMP-3 VALUE 100000.
01 WX-SUELDO-NUM   PIC 9(6).

MOVE WX-SUELDO-COMP3 TO WX-SUELDO-NUM

Sin problema. WX-SUELDO-NUM valdrá 100000.

Ejemplo 7.
01 WX-SUELDO-COMP3 PIC S9(6) COMP-3 VALUE 100000.
01 WX-SUELDO-ALF   PIC X(6).

MOVE WX-SUELDO-COMP3 TO WX-SUELDO-ALF

Sin problema. WX-SUELDO-ALF valdrá 100000.

Ejemplo 8.
01 WX-SUELDO-COMP PIC S9(6) COMP VALUE 100000.
01 WX-SUELDO-NUM  PIC 9(6).

MOVE WX-SUELDO-COMP TO WX-SUELDO-NUM

Sin problema. WX-SUELDO-ALF valdrá 100000.

Ejemplo 9.
01 WX-SUELDO-COMP PIC S9(6) COMP VALUE 100000.
01 WX-SUELDO-ALF  PIC X(6).

MOVE WX-SUELDO-COMP TO WX-SUELDO-ALF

Sin problema. WX-SUELDO-ALF valdrá 100000.

Ejemplo 10.
01 WX-SUELDO-COMP3 PIC S9(6) COMP-3 VALUE 100000.
01 WX-SUELDO-COMP  PIC 9(6) COMP.

MOVE WX-SUELDO-COMP3 TO WX-SUELDO-COMP

Sin problema, WX-SUELDO-COMP valdrá 100000 pero en formato hexadecimal: 186A0

Ejemplo 11.
01 WX-SUELDO-COMP  PIC 9(6) COMP VALUE 100000.
01 WX-SUELDO-COMP3 PIC S9(6) COMP-3.

MOVE WX-SUELDO-COMP TO WX-SUELDO-COMP3

Sin problema, WX-SUELDO-COMP valdrá 100000.

Para campos numéricos existe también la sentencia COMPUTE para pasar información de uno a otro:
Ejemplo 12.
01 WX-NUMERO-1 PIC 9(5) VALUE 12345.
01 WX-NUMERO-2 PIC 9(5).

COMPUTE WX-NUMERO-2 = WX-NUMERO-1

Ahora WX-NUMERO-2 valdrá 12345.

Aunque la sentencia COMPUTE se usa en general para operaciones aritméticas: suma, resta, multiplicación, división, potencia...

COMPUTE WX-NUMERO1 =
((WX-NUMERO2 + WX-NUMERO3) - (WX-NUMERO4 * WX-NUMERO5) / WX-NUMERO6) ** 2


Y se aplican las mismas reglas de prioridades que en matemáticas.(el doble asterisco es la potencia)

Es en estos casos cuando un campo numérico que lleve caracteres o espacios pierde su sentido, pues adivinad que sucede si intentamos dividir 'ABC?%&' entre 1000 al ejecutar el programa...
Premio! Un S0C7!!


Nunca va a tener sentido dividor letras entre números, imagino que en esto estamos todos de acuerdo : P

Para operaciones aritméticas existen también:
ADD: operador suma. "ADD 1 TO WX-CAMPO"
SUBTRACT: operador resta. "SUBTRACT 1 FROM WX-CAMPO"
MULTIPLY: operador multiplicación. "MULTIPLY WX-CAMPO BY 1"
DIVIDE: operador división. "DIVIDE WX-CAMPO BY 1"
REMAINDER: es el resto de una división. "DIVIDE WX-CAMPO BY 1 REMAINDER WX-RESTO"


Ejemplo 13.
01 WX-TELEFONO-NUM PIC 9(9) VALUE  666111333.
01 WX-TELEFONO-ALF PIC X(9) VALUE '666111333'.
01 WX-TELEFONO     PIC X(6).


Para campos alfanuméricos y numéricos sin comprimir existe la posibilidad de mover sólo determinadas posiciones del campo.

MOVE WX-TELEFONO-NUM(1:6) TO WX-TELEFONO

En este caso estamos cogiendo 6 posiciones, empezando desde la posición 1. Ahora WX-TELEFONO valdrá '666111'.

MOVE WX-TELEFONO-ALF(4:) TO WX-TELEFONO

En este caso estamos cogiendo todas las posiciones hasta final de campo desde la posición 4. Ahora WX-TELEFONO valdrá '111333'.

Para mover posiciones de un campo que pertenece a un array o tabla, primero habrá que indicar el occurs o índice y después la posición:
01 WX-TABLA OCCURS 5 TIMES.
   05 WX-TELEFONO1 PIC X(9) VALUE '111111111'.
      05 WX-TELEFONO2 PIC X(9) VALUE '222222222'.
   05 WX-TELEFONO3 PIC X(9) VALUE '333333333'.
   05 WX-TELEFONO4 PIC X(9) VALUE '444444444'. 

MOVE WX-TELEFONO3(3)(4:) TO WX-TELEFONO

Ahora WX-TELEFONO valdrá '333333'.


CONCATENANDO CAMPOS

En COBOL también existe la opción de concatenar campos usando la sentencia STRING. Lo que haremos será indicar las variables a concatenar, el modo en que se unirán y el campo donde se guardará el resultado.

01 WX-CAMPO1    PIC X(17) VALUE 'CONSULTORIO COBOL'.
01 WX-CAMPO2    PIC X(10) VALUE ' SON GUAYS'.
01 WX-RESULTADO PIC X(41).

STRING 'LOS TIPOS DEL ' WX-CAMPO1 WX-CAMPO2
DELIMITED BY SIZE
INTO WX-RESULTADO





Donde DELIMITED BY SIZE indica que los campos se unirán según el tamaño que ocupen, y el INTO indicará el campo donde guardar la información concatenada.
Si por ejemplo tuviésemos campos con espacios al final podríamos indicarle un DELIMITED BY SPACES, para que cortase la información al encontrarse el primer espacio. Vamos a ver el ejemplo:

01 WX-DIA   PIC 9(2) VALUE 31.
01 WX-MES   PIC X(10).
01 WX-FECHA PIC X(19).

MOVE 'MAYO' TO WX-MES

Ahora WX-MES valdrá 'MAYO      '.

STRING 'HOY ES ' WX-DIA ' DE ' DELIMITED BY SIZE
WX-MES DELIMITED BY SPACE
'.' DELIMITED BY SIZE
INTO WX-FECHA


El resultado:
HOY ES 31 DE MAYO.


Si no hubiésemos indicado el DELIMITED BY SPACE, el resultado sería:
HOY ES 31 DE MAYO      .


Recordad: el DELIMITED BY se aplica a todos los campos que lo preceden. Usad el DELIMITED BY correcto en cada caso!

INICIALIZANDO CAMPOS

Para "vaciar" un campo podríamos moverle ceros o espacios (según su formato). Pero
en cobol existe una instrucción más sencilla que es el INITIALIZE.

01 WX-NUMERO PIC 9(5) VALUE 12345.
(...)
INITIALIZE WX-NUMERO


Ahora WX-NUMERO valdrá 00000. Si fuese alfanumérico valdría '     ' (cinco espacios).

Las variables "vacías" en cobol pueden tener 3 tipos de información:
ceros: 00000
espacios: '     '
low-values: '.....'

Por eso a la hora de preguntar si un campo está vacío en cobol preguntaremos:
IF WX-CAMPO EQUAL ZEROES OR SPACES OR LOW-VALUES
(...)


Nota: Tanto LOW-VALUES como HIGH-VALUES son palabras reservadas en cobol, y se pueden definir como "el valor más bajo posible" y "el valor más alto posible" respectivamente. Veremos su uso en otros artículos.


BUCLES Y CONDICIONES

Las condiciones en cobol se escriben de la siguiente manera:
IF WX-CAMPO = 1
   MOVE 1        TO WX-CAMPO2
ELSE
   MOVE WX-CAMPO TO WX-CAMPO2
END-IF


Podemos escribir IFs dentro de IFs (IFs anidados) y La sentencia ELSE no es obligatoria:

IF WX-CAMPO1 = 1
   IF WX-CAMPO2 = 2
      MOVE WX-CAMPO1 TO WX-CAMPO3
   END-IF
ELSE
   MOVE 1 TO WX-CAMPO3
END-IF

No tienen más ciencia que cualquier IF de cualquier otro lenguaje.

Los operadores lógicos serán OR, AND, EQUAL, NOT EQUAL.
Los operadores de mayor, menor, serán GREATER y LESS.
También se pueden utilizar combinaciones como GREATER OR EQUAL (mayor o igual), y también con símbolos: >= (mayor o igual), <= (menor o igual).

Otra forma de preguntar por una condición es con un EVALUATE.
Preguntando si se cumple una condición, es decir, si es TRUE:

EVALUATE TRUE
  WHEN WS-CAMPO1 EQUAL WS-CAMPO2
      (instrucciones a realizar)
  WHEN WS-CAMPO1 NOT EQUAL WS-CAMPO2
      (instrucciones a realizar)
  WHEN OTHER
      (instrucciones a realizar)
END-EVALUATE

Preguntando por el valor de un campo:
EVALUATE WS-CAMPO1
  WHEN 1
      (instrucciones a realizar) 
  WHEN 2
      (instrucciones a realizar) 
  WHEN OTHER
      (instrucciones a realizar) 
END-EVALUATE

Preguntando por el valor de varios campos:
EVALUATE WS-CAMPO1 ALSO WS-CAMPO2
  WHEN 1 ALSO 1
      (instrucciones a realizar) 
  WHEN 2 ALSO 2
      (instrucciones a realizar) 
  WHEN OTHER
      (instrucciones a realizar) 
END-EVALUATE

ó

EVALUATE TRUE ALSO TRUE
  WHEN WS-CAMPO1 = 1 ALSO WS-CAMPO2 = 1
      (instrucciones a realizar) 
  WHEN WS-CAMPO1 = 2 ALSO WS-CAMPO2 = 2
      (instrucciones a realizar) 
  WHEN OTHER
      (instrucciones a realizar) 
END-EVALUATE 

ó

EVALUATE WS-CAMPO1 ALSO TRUE
  WHEN 1 ALSO WS-CAMPO2 = 1
      (instrucciones a realizar) 
  WHEN 2 ALSO WS-CAMPO2 = 2
      (instrucciones a realizar) 
  WHEN OTHER
      (instrucciones a realizar) 
END-EVALUATE 

Los bucles son diferentes debido a que en cobol no existe la sentencia "while" de otros lenguajes. Aquí los bucles son siempre "UNTIL", es decir, hasta que se cumpla la condición especificada.

MOVE 0 TO WX-CAMPO

PERFORM UNTIL WX-CAMPO = 5 -> aquí es donde pregunta por la condición
   proceso a realizar
   ADD 1 TO WX-CAMPO
END-PERFORM


El proceso se realizará para:
WX-CAMPO = 0
WX-CAMPO = 1
WX-CAMPO = 2
WX-CAMPO = 3
WX-CAMPO = 4

MOVE 5 TO WX-CAMPO

PERFORM UNTIL WX-CAMPO = 5 -> aquí es donde pregunta por la condición
   proceso a realizar
END-PERFORM


El proceso no se llega a realizar.

Si quisiésemos preguntar por la condición al final del bucle, tendríamos que codificar el bucle con un "WITH TEST AFTER":

MOVE 0 TO WX-CAMPO


PERFORM WITH TEST AFTER
  UNTIL WX-CAMPO = 5
   proceso a realizar
   ADD 1 TO WX-CAMPO
END-PERFORM -> aquí es donde pregunta por la condición

El proceso se realizará para:
WX-CAMPO = 0
WX-CAMPO = 1
WX-CAMPO = 2
WX-CAMPO = 3
WX-CAMPO = 4

MOVE 5 TO WX-CAMPO

PERFORM WITH TEST AFTER
  UNTIL WX-CAMPO = 5
   proceso a realizar

   ADD 1 TO WX-CAMPO
END-PERFORM -> aquí es donde pregunta por la condición

El proceso se realiza una vez, antes de comprobar si se cumple la codición.

Como veíamos en la estructura general de un programa cobol, un párrafo entero también se puede repetir hasta que se cumpla una condición:

PERFORM 2000-PROCESO
  UNTIL CUMPLA-CONDICION

Otro tipo de bucles son los "PERFORM VARYING". En este caso tendremos un contador que se irá incrementando automáticamente según le indiquemos:

PERFORM VARYING WI-INDICE FROM 1 BY 1 UNTIL WI-INDICE = 10
  código a ejecutar
END-PERFORM

El "FROM" le indica desde que valor empezar. El "BY" le indica cuanto le ha de sumar cada vez.
Como en el otro caso, el "UNTIL" le indicará la condición de final de bucle.

Con todo esto, ya podemos empezar a ver un ejemplo de un programa sencillo : )

18 comentarios:

Anónimo dijo...

parece interesante,deja leerlo bien y ponerlo en pràctica,habia estado buscando un manual asì,muchas gracias

Anónimo dijo...

BUENA INFORMACIÓN, GRACIAS! ;)

Anónimo dijo...

Encontré un errorcillo: en vez de REMINDER en la división, debe ser REMAINDER

Tallian dijo...

Corregido! Gracias : )

Juan dijo...

Resulta que me han contratado para trabajar con Cobol y DB2 pero en mi vida había trabajado con ello y este curso me está siendo de gran ayuda. Gracias y enhorabuena por el curso.

Tallian dijo...

De nada Juan. Nos alegramos de que te sea útil. Pregunta lo que necesites!
Talli.

Gustavo Mella dijo...

muy bueno muchachos! muy bien explicado! gracias!

david jimenez dijo...

HOLA ALGUINE PODRIA AYUDARME PARA ENTENDER ESTE PERFORM :. PERFORM CLEAR-CNTRL-TABLE-1 THRU CLEAR-CNTRL-TAB-2
VARYING X-1 FROM 1 BY 1 UNTIL
X-1 GREATER THAN 25
AFTER IND1 FROM 1 BY 1 UNTIL
IND1 GREATER THAN NUM-MARKETS
AFTER IND2 FROM 1 BY 1 UNTIL
IND2 GREATER THAN 2. GRACIAS

Tallian dijo...

Hola David.
Si no me equivoco, el AFTER lo que hace es que por cada valor del índice X-1, incrementa de 1 en 1 el índice IND1. Algo así como un bucle dentro de un bucle.
Lo mismo el segundo AFTER.

Un saludo!

Unknown dijo...

El ejemplo 5 me ha venido de maravillas. Muchas gracias!!

bto dijo...

Que tal, solo para aportar... En EVALUATE también se puede dar la combinación:

EVALUATE WS-CAMPO1 ALSO TRUE

Saludos desde México.

Tallian dijo...

Gracias bto! Lo añado :-)

Francisco Garrigós Pardo dijo...

Este comentario ha sido eliminado por el autor.

Francisco Garrigós Pardo dijo...

Saludos y muchas gracias por este tutorial que me está poniendo al día para mi inminente futuro trabajo. Un comentario en cuanto al output del ejemplo del WITH TEST AFTER ya que pienso que la variable WX-CAMPO tomará los valores del 0 al 5 y no sólo al 4 como haría SIN la cláusula WITH TEST AFTER. mUchas gracias.

Francisco Garrigós Pardo dijo...

Saludos. En el ejemplo 13 creo que en vez de la sentencia MOVE WX-TELEFONO-ALF(3)(4:) TO WX-TELEFONO se trata de MOVE WX-TABLA(3)(4:) TO WX-TELEFONO y también sería válida MOVE WX-TELEFONO3(4:) TO WX-TELEFONO ¿es esto correcto?

Tallian dijo...

Hola Francisco.
Sobre el ejemplo 13, efectivamente estaba mal, lo he corregido. Gracias!
Sobre el "with test after", te explico:
PERFORM WITH TEST AFTER
UNTIL WX-CAMPO = 5
proceso a realizar
ADD 1 TO WX-CAMPO
END-PERFORM -> aquí es donde pregunta por la condición
Cuando pregunto por la condición "until wx-campo = 5", ya he añadido 1 a wx-campo, por lo tanto en cuanto toma valor 5 finaliza el bucle :-)

Sebas dijo...

Hola. Una duda.
¿Habría algún problema en hacer la siguiente resta donde difieren tanto la precisión de la parte entera como la precisión de los decimales de ambos operandos?

01 WK-RESTA PIC S9(12)V9(08).
01 WK-CAMBIO1 PIC S9(08)V9(05).
01 WK-CAMBIO2 PIC S9(12)V9(08).

COMPUTE WK-RESTA = WK-CAMBIO1 - WK-CAMBIO2

Muchas gracias.

Tallian dijo...

Ningún problema Sebas. Puesto que el resultado lo estás guardando en el formato del número de mayor valor. De esa forma no perderás ni decimales ni enteros.
01 WK-RESTA PIC S9(12)V9(08).
01 WK-CAMBIO1 PIC S9(08)V9(05) VALUE 99999999,99999.
01 WK-CAMBIO2 PIC S9(12)V9(08) VALUE 111111111111,11111111.
COMPUTE WK-RESTA = WK-CAMBIO1 - WK-CAMBIO2
WK-RESTA:1110111111111111211J
:-)