Writing Programs | System Limits and Programming Restrictions |
COBOL is a very flexible language. It contains many features that make it easy for you to create successful applications. This chapter introduces some of the more advanced language features that are available with COBOL.
Applying these features to common programming problems enables you to write more efficient, more user-friendly (and in some cases simpler) applications.
Topics that are discussed include:
A recursive routine is a routine that calls itself. Recursion is the most natural method of solving certain problems, such as:
Although you can write any recursive routine as a straight iterative routine, the use of a recursive routine can simplify the program's logic.
This section shows you how you can create recursive programs with this COBOL system.
In this COBOL system, a program can be recursive provided it is not nested inside another program and it has a Local-Storage Section. A program can call itself via either its program-id or an entry point. The example below illustrates recursion in COBOL. It reads a table of a person's descendants, and displays the names of those who have no children.
Each call of a recursive routine is called an instance. Each instance needs a different set of the data items used by the routine; so this COBOL system enables you to have a Local-Storage Section. Every time a new instance of the routine starts, a new, initialized copy of this section is created in memory; this is deleted when the instance finishes. You get a run-time error message if you try to use recursion in a program that does not contain a Local-Storage Section.
In the following example, the table in the item family-tree
contains an entry for each of Fred Smith's descendants. Assume the data
has been put in the table by an earlier program. The first entry is Fred
Smith's own. Each entry contains, as well as the person's name, a data
item called eldest-pointer
containing the position in the
table of the entry for that person's eldest child. Someone with no
children has 99 in this item. Similarly each entry has a data item sibling-pointer
pointing to the entry for the person's next younger sibling. Someone with
no younger siblings has 99 in this item.
identification division. program-id. family. . . . working-storage section. 01 family-tree. 03 individual occurs 50. 05 ind-name pic x(30). 05 eldest-pointer pic 9(2). 05 sibling-pointer pic 9(2). local-storage section. 01 tree-pointer pic 9(2). linkage section. 01 parent-pointer pic 9(2). procedure division. move 1 to tree-pointer call "children" using tree-pointer stop run.
* If this person has no eldest child, routine "children" * displays the person's name and does nothing more. Otherwise, * this routine starts with the person's eldest child and calls * itself for each sibling in turn. entry "children" using parent-pointer move eldest-pointer(parent-pointer) to tree-pointer if tree-pointer = 99 display ind-name(parent-pointer) else perform until tree-pointer = 99 call "children" using tree-pointer move sibling-pointer(tree-pointer)to tree-pointer end-perform end-if.
The following sample code illustrates some of the features of a
recursive routine. This routine, identified by the
entry point factorl
, computes the factorial of a
number that you enter.
Example
Program that illustrates a recursive routine.
1 working-storage section. 2 01 n pic x(4) comp-x. 3 01 factorial pic x(4) comp-x. 4 01 m pic x(4) comp-x. 5 6 local-storage section. 7 8 procedure division. 9 accept n 10 move 1 to factorial 11 call "factorl" using n 12 display "factorial of " n " is " factorial 13 stop run. 14 15 entry "factorl" using m. 16 if m < 1 17 move 1 to factorial 18 else 19 if m > 1 20 multiply m by factorial 21 subtract 1 from m 22 call "factorl" using m 23 end-if 24 end-if 25 26 exit program.
Line 6:
local-storage section
Since the recursive routine can be called several times, the local data from one call must be protected from another call to the same code. This protection is done using a Local-Storage Section. Each time a new instance of the routine starts, an initialized copy of this section is created in memory. If you try to use recursion in a program that does not contain a Local-Storage Section, you get an RTS error message.
Notice that this routine does not have any local storage data items, but it still requires the local storage declaration.
Line 15:
entry 'factorl' using m.
This is the entry point of the recursive routine factorl
.
A program can call itself via its Program-ID or an entry point.
Lines 19-23:
if m > 1 multiply m by factorial subtract 1 from m call 'factorl' using m end-if
This loop contains the recursive call.
Line 22:
call 'factorl' using m
The recursive call statement.
For a more extensive illustration of recursive routines, refer to the sample program diophant.cbl in the demo subdirectory within your COBOL system directory. This program solves the linear equation Ax + By = 0 for integers x and y.
You can manipulate data items in detail using COBOL. Two features that enable this are the STRING verb and reference modification.
STRING concatenates the partial or complete contents of two or more data items and stores the result.
Reference modification enables you to reference (and change) parts of a data item by starting values, for a specified length.
In general, the use of STRING is easier to read and implement. However, it can be resource-intensive, particularly if used many times in a program. You can use STRING in programs that do not incur a lot of overhead for other reasons, such as significant file I/O.
Reference modification is more efficient, but can lead to code that is more difficult to read and maintain. It is a way to improve performance in programs that use STRING heavily and are running slowly or growing too large.
For example, the following two statements are equivalent. In both
examples, street-address
is 25 bytes long:
string street-address delimited by size into print-rec with pointer num-char
move street-address to print-rec (num-char: 25)
For more information on the STRING statement and reference modification, see your Language Reference.
An intrinsic function enables you to reference a data item whose value is determined at the time the data item is referenced during the execution of the statement.
As an example of an intrinsic function, consider the following statement:
compute x = function cos(y)
The intrinsic function cos
computes a numeric value that
approximates the cosine of an angle (expressed in y
radians)
and stores that value in x
.
This example shows one way of using intrinsic functions, that is assigning the value of the function to a data item. There are many more.
Example
The following sample program, intrins.cbl, shows some of the ways you can use intrinsic functions in your programs. This sample program is in the demo subdirectory within your COBOL system directory.
Note: There are three types of intrinsic functions: integer, numeric and alpha. The FACTORIAL function used in the sample below is a numeric function. The coding technique shown might not apply for the other function types.
1$set mf noosvs ans85 2 3************************************************************* 4* Copyright Micro Focus Limited 1991. All Rights Reserved. * 5* This demonstration program is provided for use by users * 6* of Micro Focus products and may be used, modified and * 7* distributed as part of your application provided that you * 8* properly acknowledge the copyright of Micro Focus in this * 9* material. * 10************************************************************* 11 12************************************************************* 13* * 14* INTRINS.CBL * 15* * 16* This program demonstrates some of the ways you can * 17* use Intrinsic Functions in your COBOL application. * 18* This program uses the FACTORIAL Intrinsic Function * 19* to illustrate the following capabilities: * 20* * 21* 1) Data item is assigned the value of a function * 22* 2) Function is used as a data item in an EVALUATE * 23* statement * 24* 3) Function is used as a data item in an IF * 25* statement * 26* 4) Function uses an array element (fixed index) as * 27* an argument * 28* 5) Function uses an array element (variable index) * 29* as an argument * 30* 6) Data item is assigned the value of a function of * 31* a function * 32* 7) Data item, assigned the value of the function, * 33* is used in a COMPUTE statement * 34* 8) Data item is assigned the value of the sum of * 35* two functions * 36* 9) Function is used in the UNTIL condition of a * 37* PERFORM ... UNTIL statement * 38* * 39* * 40* To familiarize yourself with the Intrinsic function * 41* syntax, try running INTRINS under Animator. * 42* * 43* Compile the program using: * 44* * 45* COBOL INTRINS ANIM; * 46* * 47* then animate the program: * 48* * 49* ANIMATE INTRINS * 50* * 51* * 52* For more information see your Language Reference * 53* and PC Programmer's Guide. * 54* * 55************************************************************* 56 working-storage section. 57 78 fals value 0. 58 78 tru value 1. 59 60 01 true-or-false pic 9(1). 61 62 01 factor pic s9(10). 63 64 01 val pic s9(10). 65 66 01 indx pic 9(5) comp-x. 67 68 01 arg pic 9(2) comp-x value 5. 69 70 01 arr value "40537". 71 03 elem occurs 5 times pic 9. 72 73 procedure division. 74 75 main-section. 76 77************************************************************ 78* Form 1 - Data item is assigned the value of the function * 79************************************************************ 80 81 compute factor = function factorial(0) 82 83************************************************************ 84* Form 2 - Function is used as a data item in an EVALUATE * 85* statement * 86************************************************************ 87 88 evaluate function integer(6.5) 89 when 6 90 move tru to true-or-false 91 when other 92 move fals to true-or-false 93 end-evaluate 94 95************************************************************ 96* Form 3 - Function is used as a data item in an IF * 97* statement * 98************************************************************ 99 100 if function integer (function factorial(arg)) = 120 then 101 move tru to true-or-false 102 else 103 move fals to true-or-false 104 end-if 105 106 ************************************************************ 107 * Form 4 - Function uses an array element (fixed index) as * 108 * an argument * 109 ************************************************************ 110 111 compute factor = function factorial(elem(4)) 112 113 ************************************************************ 114 * Form 5 - Function uses an array element (variable index) * 115 * as an argument * 116 ************************************************************ 117 118 move 4 to indx 119 compute factor = function factorial(elem(indx)) 120 121 ************************************************************ 122 * Form 6 - Data item is assigned the value of a function * 123 * of a function * 124 ************************************************************ 125 126 compute factor = function factorial( 127 function factorial(3)) 128 129 ************************************************************ 130 * Form 7 - Data item, assigned the value of the function, * 131 * is used in a COMPUTE statement * 132 ************************************************************ 133 134 compute val = function factorial(3) + 5 135 136 ************************************************************ 137 * Form 8 - Data item is assigned the value of the sum of * 138 * two functions * 139 ************************************************************ 140 141 compute val = function factorial(3) + 142 function factorial(5) 143 144 ************************************************************ 145 * Form 9 - Function is used in the UNTIL condition of a * 146 * PERFORM ... UNTIL statement * 147 ************************************************************ 148 149 move 1 to indx 150 perform para-1 until function integer (function 151 factorial(indx)) = 120 152 stop run. 153 154 para-1. 155 compute indx = indx + 1.
Line 81:
compute factor = function factorial(0)
Data item is assigned the value of the function
Lines 88-93:
evaluate function integer(6.5) when 6 move tru to true-or-false when other move fals to true-or-false end-evaluate
Function is used as a data item in an EVALUATE statement
Lines 100-104:
if function integer (function factorial(arg)) = 120 then move tru to true-or-false else move fals to true-or-false end-if
Function is used as a data item in an IF statement
The result of a numeric function is in floating point format, so it cannot be expected to hold an exact integer value. In this example, the integer function is used to obtain an exact integer value for the IF statement.
Line 111:
compute factor = function factorial(elem(4))
Function uses an array element (fixed index) as an argument
Lines 118-119:
move 4 to indx compute factor = function factorial(elem(indx))
Function uses an array element (variable index) as an argument
Lines 126-127:
compute factor = function factorial (function factorial(3))
Data item is assigned the value of a function of a function
Line 134:
compute val = function factorial(3) + 5
Data item, assigned the value of the function, is used in a COMPUTE statement
Lines 141-142:
compute val = function factorial(3) + function factorial(5)
Data item is assigned the value of the sum of two functions
Lines 149-155:
move 1 to indx perform para-1 until function integer (function factorial(indx)) = 120 stop run. para-1. compute indx = indx + 1.
Function is used in the UNTIL condition of a PERFORM ... UNTIL statement
In addition to this sample program described above, there is a second sample program, functest.cbl, which shows examples of some of the other intrinsic functions. THis sample program is in the demo subdirectory within your COBOL system directory.
For more information on intrinsic functions, see your Language Reference.
X/Open specifies syntax providing the function-names ENVIRONMENT-NAME and ENVIRONMENT-VALUE. You can DISPLAY UPON ENVIRONMENT-NAME which contains an environment variable name and subsequently ACCEPT FROM or DISPLAY UPON ENVIRONMENT-VALUE which contains an environment variable value.
Specifying these function-names enables you to dynamically alter the environment variable settings at run-time.
Using the X/Open syntax you can read and update the environment space used by your application, thereby changing the logical file associated with a physical file. You then use COBOL I/O syntax to read and write to and from this file in the normal way.
DOS, Windows and OS/2:
On DOS, Windows and OS/2, Mfextmap supports this method, enabling you to
read and write to environment variables using X/Open syntax, which is
supported by default. However, you can use this syntax without the mfextmap.dat
file. Disabling the search for the mapper file is described in the section
Disabling Mapper File Support.
16-bit:
On the 16-bit COBOL system, if you are using X/Open syntax without
Mfextmap installed, writing to environment variables takes the ON
EXCEPTION route.
DOS, Windows and OS/2:
On DOS, Windows and OS/2, once the File Mapper is active, you
cannot disable it in favor of returning to just searching the environment
space. If you are using the mapper file, you can change the mapper file
during the execution of the program. The mapping information in the
current version of the mapper file will then be used. This allows you to
easily change the data being used to test the program.
DOS, Windows and OS/2:
On DOS, Windows and OS/2, environment variables whose values you set or
change using the External File Mapper are only set while the COBOL program
(or any other COBOL program in the run unit) is executing. When program
execution is complete, the value of the environment variable reverts to
its previous setting.
16-bit:
Using X/Open syntax in the External File Mapper to set environment
variables is available only on the 16-bit COBOL system when using the
shared run-time system COBLIB.
Any environment variables set or changed during an application's run are valid only during that run, and will revert to the original values upon the application's termination. You can have up to a maximum of 64 Kilobytes (or the machine's free space, whichever is smaller) of written environment variables at any one time during the run of an application.
Example
DOS, Windows and OS/2:
The following example program for DOS, Windows and OS/2, uses X/Open
syntax to show how the value of an environment name can be changed when
Mfextmap is installed. It shows how, if the environment name refers to an
external filename, the file the external filename references can be
changed during program execution.
Before executing this program, you must set FILENAME=FILE1 at the operating system command prompt, so that "filename" has a value.
working-storage section. 01 env pic x(40). procedure division. call "mfextmap" * read the value of environment name "filename" display "filename" upon environment-name accept env from environment-value * display the read value display "filename = " env * set the value of environment name "filename" to FILE2 display "filename" upon environment-name display "FILE2" upon environment-value * read the value of environment name "filename" display "filename" upon environment-name accept env from environment-value * display the read value to show its changed display "filename = " env
* leave the program exit program stop run.
DOS, Windows and OS/2:
The mapper file is supported on DOS, Windows and OS/2 only.
By disabling the search for the mapper file, you will increase the speed of your system as no disk accesses are required to find the current mapper file.
16-bit:
On the 16-bit COBOL system, to disable the search for the mapper file (mfextmap.dat),
specify the following in the environment space via the SET command before
starting your application:
set mfextmap=no
32-bit:
On 32-bit COBOL systems, to disable the search for the mapper file (mfextmap.dat),
set the run-time tunable environment_mapper
to FALSE.
While programming in a COBOL environment, you deal with data with characters, fields, records, or files. However, there are occasions when you must deal with individual data bits. For example, bit manipulation is an important feature in applications dealing with:
The run-time system has a set of COBOL system library routines that handle bit manipulation operations easily.
The following truth table illustrates the logical operations:
A | 0 | 0 | 1 | 1 | |
B | 0 | 1 | 0 | 1 | |
CBL_NOT | (not A) | 1 | 1 | 0 | 0 |
CBL_AND | (A AND B) | 0 | 0 | 0 | 0 |
CBL_OR | (A OR B) | 0 | 1 | 1 | 1 |
CBL_EQ | (A EQ B) | 1 | 0 | 0 | 1 |
CBL_XOR | (A XOR B) | 0 | 1 | 1 | 0 |
CBL_IMP | (A IMP B) | 1 | 1 | 0 | 1 |
To see how you use this table, assume you are comparing bits A and B, using the equivalence operation CBL_EQ. From the table you can see the resultant bit is set to 1 if both A and B are 0 or if both A and B are 1. Otherwise the resultant bit is set to 0.
The following data items are used in the sample syntax below:
Data item |
Description |
source |
The sending field; a data item of any format. |
target |
The receiving field; a data item of any format. |
length |
Gives the number of bytes of source
and target to be used, starting from the leftmost
byte |
Except for the CBL_NOT routine, all the routines perform logical
operations on the bits of two data items. They combine bits from the two
data items and return the result in the second item according to the truth
table above. The operation starts at the left-hand end of the items. The
length
operand determines how many bytes are operated on.
Remaining bytes at the right-hand end of the target
item
are unchanged.
Warning: If the length
is longer than either data
item, bytes immediately following the data items are used up to the length
specified.
The syntax for the CBL_NOT routine is:
call "CBL_NOT" using target by value length
where:
CBL_NOT |
Does a logical NOT on the bits of a data item. |
The syntax for the remaining bit manipulation routines is:
call "log_oper" using source target by value length
where log_oper
is one of:
DOS, Windows and OS/2:
On DOS, Windows and OS/2, a sample program, logoper.cbl, is
available in the demo subdirectory within your COBOL system
directory. It illustrates the use of bit manipulation routines. This
program uses three of the logical call-by-name routines: CBL_OR, CBL_AND,
and CBL_XOR. You might want to review this program with this discussion.
In the sample program, the section blink-cursor
makes the
cursor character (the character the cursor is pointing to) blink by its
attributes.
To see how you would create code to make the cursor blink, first consider the form of the display attribute. The following table shows the structure of the attribute byte for a personal computer with a mono display.
Bit
|
Attribute
|
---|---|
7 | Blink |
6-4 | Turns off display or sets reverse video |
3 | Intensity |
2-0 | Normal or underline select |
Thus, for example, setting bit 7 to 1 turns blinking on.
In the Working-Storage Section of the program, the mask for the "blink" attribute is defined as:
01 blink-mask pic x value x"80".
This hexadecimal value translates to the following bit pattern:
1 0 0 0 0 0 0 0
The CBL_READ_SCR_ATTRS routine reads the current attributes of the screen into an attribute buffer that in this case is one character in length.
call "CBL_READ_SCR_ATTRS" using csr-pos csr-attr csr-length
The CBL_OR routine does a logical OR on the current attributes and the blinking mask. This in effect turns on the blinking attribute. Note that the length parameter is 1. This says the OR operation is for one byte.
call "CBL_OR" using blink-mask csr-attr by value 1
The CBL_WRITE_SCR_ATTRS routine writes the updated attributes buffer to the screen causing the character to "blink".
call "CBL_WRITE_SCR_ATTRS" using csr-pos csr-attr csr-length
The following routines are available for logic operations:
CBL_AND |
Logical AND |
CBL_EQ |
Logical EQuivalence |
CBL_IMP |
Logical IMPlies |
CBL_NOT |
Logical NOT |
CBL_OR |
Logical OR |
CBL_XOR |
Logical eXclusive OR |
Additional routines for handling bits:
X"F4" |
Pack Byte |
X"F5" |
Unpack Byte |
The logic routines carry out logic operations on bits. Apart from CBL_NOT, all these operations have two operands.
If the length is specified as a literal and there is no RETURNING clause then native code is optimized to generate in-line code.
In the two-operand routines, interchanging the two operands, source and target, does not change the result except in CBL_IMP. However, the result is always stored in the second operand, target.
If length is longer than either data item, bytes following that data item are used, up to the length specified.
The parameter length can be replaced by the syntax:
length of source
or:
length of target
assuming all the bytes of the data item are to be used.
Logical AND and OR operations can also be carried out using the VALUE clause.
RETURN-CODE is not affected by these routines.
Does a logical AND between the bits of two data items.
call "CBL_AND" using source target by value length
source |
Any data item |
target |
Any data item. |
length |
Numeric literal or pic x(4) comp-5. |
source |
One of the data items to AND. |
target |
The other data item to AND. |
length |
The number of bytes of source and target to AND. Positions in target beyond this are unchanged. |
target |
The result. |
The routine starts at the left-hand end of source and target and ANDs the bits together, storing the result in target. The truth table for this is:
source | target | Result |
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
Introduction to Logic Routines
Does a logical EQUIVALENCE between the bits of two data items.
call "CBL_EQ" using source target by value length
source |
Any data item. |
target |
Any data item. |
length |
Numeric literal or pic x(4) comp-5. |
source |
One of the data items to EQUIVALENCE. |
target |
The other data item to EQUIVALENCE. |
length |
The number of bytes of source and target to EQUIVALENCE. Positions in target beyond this length are unchanged. |
target |
The result. |
The routine starts at the left-hand end of source and target and EQUIVALENCEs the bits together, storing the result in target. The truth table for this is:
source | target | Result |
0 | 0 | 1 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
Introduction to Logic Routines
Does a logical IMPLIES between the bits of two data items.
call "CBL_IMP" using source target by value length
source |
Any data item. |
target |
Any data item. |
length |
Numeric literal or pic x(4) comp-5. |
source |
One of the data items to IMPLIES. |
target |
The other data item to IMPLIES. |
length |
The number of bytes of source and target to IMPLIES. Positions in target beyond this length are unchanged. |
target |
The result. |
The routine starts at the left-hand end of source and target and IMPLIES the bits together, storing the result in target. The truth table for this is:
source | target | Result |
0 | 0 | 1 |
0 | 1 | 1 |
1 | 0 | 0 |
1 | 1 | 1 |
Introduction to Logic Routines
Does a logical NOT on the bits of a data item.
call "CBL_NOT" using target by value length
target |
Any data item. |
length |
Numeric literal or pic x(4) comp-5. |
target |
The data to operate on. |
target |
The data with the bits inverted. |
length |
The number of bytes of target to change. Positions beyond this are unchanged. |
The routine starts at the left-hand end of target and inverts bits. The truth table for this is:
Before | After |
0 | 1 |
1 | 0 |
Introduction to Logic Routines
Does a logical OR between the bits of two data items.
call "CBL_OR" using source target by value length
source |
Any data item. |
target |
Any data item. |
length |
Numeric literal or pic x(4) comp-5 |
source |
One of the data items to OR |
target |
The other data item to OR. |
length |
The number of bytes of source and target to OR. Positions in target beyond this are unchanged. |
target |
The result. |
The routine starts at the left-hand end of source and target and ORs the bits together, storing the result in target. The truth table for this is:
source | target | Result |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
Introduction to Logic Routines
Does a logical XOR between the bits of two data items.
call "CBL_XOR" using source target by value length
source |
Any data item. |
target |
Any data item. |
length |
Numeric literal or pic x(4) comp-5. |
source |
One of the data items to exclusive-OR. |
target |
The other data item to exclusive-OR. |
length |
The number of bytes of source and target to exclusive-OR. Positions in target beyond this are unchanged. |
target |
The result. |
The routine starts at the left-hand end of source and target and exclusive-ORs the bits together, storing the result in target. The truth table for this is:
source | target | Result |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
Introduction to Logic Routines
Packs the least significant bits in eight bytes into a single byte.
call x"F4" using byte array
byte |
pic x comp-x |
array |
pic x comp-x occurs 8 |
array |
The bits to be packed. |
byte |
The packed byte. |
The routine takes the eight bytes from array and uses the least significant bit of each byte to form byte. The first occurrence in array becomes the most significant bit of byte (bit 7).
00000001 00000001 00000000 00000000 00000001 00000000 00000001 00000001 | | | |+-------+ | | | | | +-------+||+---------------+ | | | +---------------+||||+-----------------------+ | +-----------------------+||||||+-------------------------------+ ¡¡¡¡¡¡¡¡ 11001011
Unpacks the bits in a byte into eight bytes.
call x"F5" using byte array
byte |
pic x comp-x |
array |
pic x comp-x occurs 8 |
byte |
The byte to be unpacked |
array |
The unpacked bits |
The routine takes the eight bits of byte and moves them to the corresponding occurrence within array:
10110011 +-----------------------+||||||+-------------------------------+ | +---------------+||||+-----------------------+ | | | +-------+||+---------------+ | | | | | |+-------+ | | | ¡ ¡ ¡ ¡ ¡ ¡ ¡ ¡ 00000001 00000000 00000001 00000001 00000000 00000000 00000001 00000001
Copyright © 1998 Micro Focus Limited. All rights reserved.
This document and the proprietary marks and names
used herein are protected by international law.
Writing Programs | System Limits and Programming Restrictions |