01 g1.
05 h1 PIC X VALUE 'N'.
88 s1 VALUE 'Y'.
88 s2 VALUE 'N'.
In the above code what will be the value of s1 and s2? whether it holds the value given in the group variable(05) or it will have their own values?
S1 and S1 are named conditions. They will be true or not true depending upon the value of H1 (or G1 in this case).
The code:
Set S1 to true
will cause the value of H1 (and G1 in the case of your specific group) to be 'Y'. If you execute:
Set S2 to true
the value of H1 (and G1 again) will be a character 'N'.
These can be tested using standard relational conditions. For example:
Evaluate true
when S1
Display "S1 is true"
when S2
Display "S2 is true"
End-Evaluate
If S1
Display "S1 is true"
Else
Display "S1 is false"
End-If
Bruno covered most of the important features of 88-levels, or named conditions, but I feel it is important to mention the way they are badly abused by Cobol programs that just can't give up their 1974 skills.
You will often see people doing things like:
Move 'Y' to H1
This is a really bad idea for several reasons:
- someday, somebody is going to "move 'x' to H1" and really mess up your day
- somebody is going to write code like "if H1 = 'Y'" and make it impossible to scan for uses of your named condition
There is a way to avoid this, use an unnamed byte with your named conditions. If your data item looks like this:
01 G1
02 ...
02 Filler Pic X value 'N'.
03 S1 value 'Y'.
03 S2 value 'N'.
By skipping the name on H1, you FORCE other programmers working with your data layout to use your named conditions S1 and S2. This has many benefits, chief among them is that you can always scan your source repository for the named conditions and you can identify all changes easily.
s1 and s2 don't hold a value. They are "named conditions" ( so-called 88-levels ) and are associated with another item ( the conditional variable ). The 88 level does not define a field, and does not take space in the record; it is merely a value definition.
The named condition can be used in an IF statement, and tests whether the conditional variable is equal to any of the values given in the named condition's VALUE clause.
The SET statement can be used to make a named condition TRUE (by assigning the first of its values to the conditional variable).
Usage:
SET s1 TO TRUE
h1 will hold the value 'Y'
You could test it's value with
IF h1 = 'Y' or simply IF s1
EDIT: As Joe Zitzelberger mentioned in his answer, the correct way to test the conditional variable is to use the named conditions.
IF s1 THEN
//do something
ELSE
//do somethingElse
END-IF
Related
I understand that HIGH-VALUES correspond to the highest in the collating sequence, however I do not understand why it may be a preferred method when using conditionals.
Example:
01 StudentRecord.
88 EndOfStudentFile VALUE HIGH-VALUES.
02 StudentID PIC X(7).
02 FILLER PIC X(23).
...
AT END SET EndOfStudentFile TO TRUE
Why not simply use VALUE 0 and SET EndOfStudentFile to 1 ?
Whats the advantage of using HIGH-VALUES in these cases?
Appreciate any input on this matter...
The conditional 88 in your example is for the StudentRecord, so it sets/queries that. I think that it may be more appropriate to use VALUE ALL HIGH-VALUES - as it stands it will set the first byte to HIGH-VALUE and then pad the record (with spaces).
VALUE 0/1 would not be possible for that as the record - because it is a group - is alphanumeric, and should not be assigned a numeric value.
... the question "is xyz preferred" is often more a question of style and only rarely "best practice". The commonly good thing is to ensure a consistent use/style so that others reading the code can understand it better.
In this specific case it could be used to "store" the information "all students were processed" which then can be queried later via IF EndOfStudentFile and if for some reason there is another START >= StudentID (I assume that is an ORGANIZATION INDEXED file here) on the file it likely will not found "another" record (still possible here, a student with an id containing ALL HIGH-VALUE would be found).
Just to clarify '88' levels do not represent real storeage.
They are conditionals which refer to the immediately preceding variable definition.
So:
If EndOfStudentFile..
is just as shortcut for
If StudentRecord is equal to High-Values...
01 ws-var-05 pic x value 'n'.
88 ws-var-88 value 'y'
01 ws-var-2 pic 9 value 1.
88 ws-var-88-2 value 2.
.
.
.
* comment ws-var-88-2 is set to true when eof is
* reached in the at end clause of read statement
* need to understand when ws-var-05 evaluates to
* true! Is this right syntax? What happens if we use
* this syntax? Need to understand if this is a
* defect
Perform 1000-para until ws-var-88-2 or ws-var-05.
The line...
Perform 1000-para until ws-var-88-2 or ws-var-05.
...contains a syntax error, at least when compiled with GNU COBOL 1.1.0.
The UNTIL clause of the PERFORM verb can contain conditional expressions. One type of conditional expression is a "condition-name condition" which is an 88-level. However, the name of an identifier (in this case ws-var-05) must be followed by a conditional operator (<, >, =, etc.) and then either another identifier or a literal for the UNTIL clause to be valid.
Is this right syntax?
No.
What happens if we use this syntax?
A compile-time error will occur.
Several things.
It is very much a "best practice" to leave the storage that backs your conditional items (88s) unnamed. This prevents people from accidentally moving the wrong value into them and causing subtle bugs.
For example, this:
01 ws-var-05 pic x value 'n'.
88 ws-var-88 value 'y'
01 ws-var-2 pic 9 value 1.
88 ws-var-88-2 value 2.
Is better written as this:
01 pic x value 'n'.
88 ws-var-88 value 'y'.
01 pic 9 value 1.
88 ws-var-88-2 value 2.
So nobody can ever accidentally do a "Move 'Y' to ws-var-05". This is especially problematic on mainframes, where there are some automatic uppercasing editor options that could really make the difference between 'y' and 'Y' hard to notice.
In the case of this, there are several things, the ws-var-05 is not a complete conditional, you could use "ws-var-88 or ws-var-88-2" and it would be a complete conditional.
You don't need a period at the end, and its use can often be problematic with modern compilers. Since Cobol-85, the END-verbname is the preferred way to terminate a command. You could write this:
Perform 1000-para until ws-var-88-2 or ws-var-05.
as:
Perform 1000-para until (ws-var-88 or ws-var-88-2)
or, IMNSHO, a clearer and cleaner approach, as it separates your terminate condition from the commands you will be executing in the body of the perform:
Perform until (ws-var-88-2 or ws-var-88-2)
Perform 1000-para
End-Perform
I will also always use parens to delimit my intended order of operations, though in this case, it matters not. For more complicated conditions, they can be a big help for the on-call programmer that has to look at something blowing up at 3am.
You trigger your terminal conditionals like so:
1000-para.
Read myfile
at end
set ws-var-88-2 to true
End-Read
If (something-else = time-to-quit)
set ws-var-88 to true
End-If
Exit. *> One of the few places you need a period
*> in modern cobol, adding the "Exit" or
*> "Continue" NOPs clearly calls out to a
*> reader that you are using a period.
I try to create a string in Cobol with individually letters. Until I try to insert a
Space, everything works. Do you have any Idea, how I could create e.x. the string
" ee ee"
?.
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 S1 PIC X(10).
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
Perform InsertSpace 2 Times
Perform InsertE 2 Times
Perform InsertSpace 2 Times
Perform InsertE 2 Times
Display S1
* expectation " ee ee"
End-Main
InsertE Section
STRING S1 DELIMITED BY SPACE
'e' DELIMITED BY SIZE
INTO S1
END-STRING
InsertSpace Section
STRING S1 DELIMITED BY SPACE
' ' DELIMITED BY SIZE
INTO S1
END-STRING
If you are trying to implement a process where one character at a time is added onto a
character variable, then something like the following might work a bit better for you:
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 S1 PIC X(10) VALUE SPACE.
01 S1-SUB PIC S9(4) BINARY VALUE ZERO.
PROCEDURE DIVISION.
PERFORM INSERT-SPACE 2 TIMES
PERFORM INSERT-E 2 TIMES
PERFORM INSERT-SPACE 2 TIMES
PERFORM INSERT-E 2 TIMES
DISPLAY '>' S1 '<'
GOBACK
.
INSERT-SPACE SECTION.
COMPUTE S1-SUB = S1-SUB + 1
MOVE SPACE TO S1 (S1-SUB : 1)
.
INSERT-E SECTION.
COMPUTE S1-SUB = S1-SUB + 1
MOVE 'E' TO S1 (S1-SUB : 1)
.
S1-SUB keeps tract of the current character position and is incremented
each time you PERFORM a section to add another character.
The above program displays: > EE EE <
Notice the trailing spaces? If you do not want these, the appropriate DISPLAY would be:
DISPLAY '>' S1 (1 : S1-SUB) '<'
which will limit the length of the display to only those characters you have explicity put into the variable. COBOL does not support variable length strings so you have to declare some PIC X type variable that can hold the maximum number of characters you want to display and then keep track of how many you have actually "used" and display only that many.
If this is the sort of thing you are looking for, I would also recommend checking
for bounds errors (ie. adding too many characters). That can be done as follows:
INSERT-E SECTION.
COMPUTE S1-SUB = S1-SUB + 1
IF S1-SUB > LENGTH OF S1
PERFORM ERROR-ROUTINE
END-IF
MOVE 'E' TO S1 (S1-SUB : 1)
.
MOVE " ee ee" TO S1
That will do what you want.
It is difficult to be certain, as you don't show what result you do get, and it is unclear what "Until I try to insert a Space, everything works" means, but...
01 S1 PIC X(10) VALUE SPACE.
Where S1 had no VALUE (and presuming you are not using a compiler which sets a default value for a PICture) the DELIMITED BY SPACE will take the whole 10 bytes, the values which a added by the STRING can never appear in the S1 unless it starts with a value of SPACE. With the value of SPACE, your four STRINGs should work. Err... no it won't, because of the SPACE, and the DELIMITED BY SPACE.
You can also use reference-modification, of course:
MOVE " " TO S1 ( 1 : 2 )
MOVE "ee" TO S1 ( 3 : 2 )
MOVE " " TO S1 ( 3 : 2 )
MOVE "ee" TO S1 ( 5 : )
Or, if you don't want to pad the final part of the field to SPACE by default, change the last to ( 5 : 2 ), which will leave bytes nine and 10 of S1 unchanged.
If you can clarify what you want to achieve, and why you think STRING is the verb to use to do it, you may get better answers.
I'm learning COBOL now and really liking the 88-type of variables, and I want to know if there are anything like them in another languages (most known languages also, such as C, Objective-C), even using a library.
The only thing I can think being similar is using
#define booleanResult (variableName==95)
But it isn't possible to set boolenResult to true and make variableName assume 95 as value.
05 nicely-named-data PIC X.
88 a-meangingful-condition VALUE "A".
88 another-meaingingful-condition
VALUE "A" "B"
"X" THRU "Z"
SPACE ZERO.
IF a-meaningful-condition
IF another-meaningful-condition
SET a-meaningful-condition TO TRUE
SET another-meaningful-condition
TO TRUE
The IFs test the value referenced by the data-name (conditional variable) that the 88 (condition name) is associated with, for a single value or one of multiple value, which can included ranges (THRU) and figurative-constants (ZERO, SPACE, LOW-VALUES, etc).
The SET, which in this form is a more recent addition to COBOL from the 1985 Standard, will change the value of the data-name to the first value specified on the 88, such that if you immediately referenced the 88 in a test, the test would be true.
COBOL does not have booleans in the sense of something resolving to 0 or 1, or anything else, being false/true.
Any language which supports Objects could be used to mimic the behaviour. Perhaps you've even done it already without really realising it.
As NealB points out in the comments, functions could be used (or a procedure, or a transfer of control to another module) but the data and references to it would not be together and protected from accidental mischief.
COBOL has great flexibility in defining data-structures. The 88-level is a powerful aid to maintaining and understanding programs, as well as writing them in the first place.
I don't know of another language which has a direct and natural element which is equivalent to this, but then there are lots of languages I don't know.
Again NealB makes an important point in the comments about the use of THRU/THROUGH to specify a range of values.
Care does need to be taken. Although the author may think that the data that they want to select can be represented by the range "010" THRU "090", they may not realise that what the compiler does is to include every single possible value in that range, by generating code for greater than or equal to "010" and less than or equal to "090".
If using THRU, ensure that your data cannot contain anything in the range which is not expected. If you mean "010" "020" "030" ... "090" that is fine, as long as the data is validated at its entry-point, so that it can never include any intervening values.
The classic example is "A" THRU "Z" on the Mainframe. We all know what the author means, but the compiler takes it literally. You cannot use "A" THRU "Z" on its own for validation, because in EBCDIC there are "gaps" between three groups of letters, and using "A" THRU "Z" would treat those gaps as true for a use of the 88.
Where the 88 level in some COBOL compilers does fall down, is in the missing "FALSE".
To re-use from the above example:
88 a-meaingingful-condition VALUE "A".
88 a-meaingingful-condition-NOT
VALUE "N".
To test the switch/flag, you use the first 88. To turn the flag.switch off, you have to use the second. Not ideal. See one of the links below for an example of FALSE on the 88 definition.
In olden times, flags/switches were set and reset with MOVE statements. As soon as the MOVE is involved, you have the same problem as you have in trying to use functions. There is no bound relationship between the MOVE and the 88-level VALUE.
These days, SET can be used to change the value of a field, to turn a flag/switch on or off.
05 FILLER PIC X.
88 a-meaingingful-condition
VALUE "A".
88 a-meaingingful-condition-NOT
VALUE "N".
The field being tested does not even need a name (it can be FILLER or omitted (an implied FILLER)).
Of course, as NealB points out in a comment on one of the links below, someone can still get at the field with a MOVE using reference-modification on a group item. So...
01 FILLER.
05 FILLER PIC X.
88 a-meaingingful-condition
VALUE "A".
88 a-meaingingful-condition-NOT
VALUE "N".
Now they can't use reference-modification even, as there is no field to name. The value of the field can only come from a VALUE clause on the definition, or from a SET statement setting one of the 88s to TRUE.
At the stage, the value that a flag/switch has, its actual value, becomes irrelevant.
01 FILLER.
05 FILLER PIC X(7).
88 a-meaingingful-condition
VALUE "APPLE".
88 a-meaingingful-condition-NOT
VALUE "BICYCLE".
Because nothing can be used to test against a literal/data-name, and the field cannot be the target of any verb except SET, you no longer have to check that all fields which say they contain N, or Y, or 0, or 1, do so, and they're not the wrong case, and no other values get placed in those fields.
I'm not suggesting the use of APPLE and BICYCLE, just using them to illustrate the point.
An 88 can also have a value expressed in hexadecimal notation, like any alpha-numeric field:
88 a-meaingingful-condition VALUE X"25".
An 88 can also be specified on a group item, typically with a figurative-constant as the value:
01 a-group-item.
88 no-more-data-for-matching VALUE HIGH-VALUES.
05 major-key PIC X(10).
05 minor-key PIC X(5).
In a file-matching process, the keys can be set to high-values at end-of-file, and the use of the keys will still cause the other file(s) to be processed correctly (keys lower than on this file).
Here are links to a number of questions from SO relating directly, or tangentially with important aspects, to 88-levels.
COBOL level 88 data type
Group variable in cobol
In Cobol, to test "null or empty" we use "NOT = SPACE [ AND/OR ] LOW-VALUE" ? Which is it?
Does a prefix of "NO" have any special meaning in a COBOL variable?
COBOL Data Validation for capital letter?
My first programming language was Cobol, now I am using c# and here is my solution to Cobol's 88 level:
In Cobol:
01 ws-valid-countries pic xx.
88 valid-country 'US', 'UK' 'HK'.
move ws-country to ws-valid-countries
if valid-country
perform...
in C#
string[] ValidCountries = {"US","UK","HK"} ;
if ( ValidCountries.Contains(newCountry.Trim().ToUpper()) )
{
// do something
Think of it as a boolean getter (essentially as in your macro) and a setter (forcing the variable to be the corresponding value). Who says COBOL wasn't modern in 1965?
As others said, just some object programming. Which is more powerful, but far less elegant. Like :
01 MY-DATASET.
05 MY-DEPARTEMENT PIC 9(2).
88 ILE-DE-FRANCE VALUES 75, 77, 78, 91 THRU 95.
Can be roughly translated in old VBA in a class named MyDataset :
Public MyDepartement As Integer
Property Get IleDeFrance() As Boolean
Dim MyArray() As Variant
MyArray = Array(75, 77, 78, 91, 92, 93, 94, 95)
IleDeFrance = UBound(Filter(MyArray, MyDepartement, True)) > -1
End Property
(just tested, it works on VBA-excel2013)
And I made the VBA as simple as possible, no clean getter or setter for the departement number, just a public data. As a class is a depot of data plus coded actions against them, you can do more things inside than a simple 88-level(that's probably why this feature didn't make up to more modern languages). But at a the price of complexity & readability.
Less elegant because the array has to be specifically defined, and testing presence in it has to be specified also. While it's inherent to the wonderful 88 level.
INPUT: WS-Variable contains '345xABCx12'
Code: IF WS-Variable string contains 'ABC' Display ' SKIP!!!' Else Perform something.
If variable contains 'abc'
display skipped
else
process-para
end if.
You are looking for the INSPECT verb... Try something like...
IDENTIFICATION DIVISION.
PROGRAM-ID. EXAMPLE.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 TESTDATA PIC X(50).
01 COUNTER PIC 9(4).
PROCEDURE DIVISION.
MOVE '12345XXABCXX12345' TO TESTDATA
MOVE ZERO TO COUNTER
INSPECT TESTDATA TALLYING COUNTER FOR ALL 'ABC'
IF COUNTER > 0
DISPLAY 'SKIP! ' TESTDATA
ELSE
DISPLAY 'DONT SKIP ' TESTDATA
END-IF
MOVE '12345XXZZZXX12345' TO TESTDATA
MOVE ZERO TO COUNTER
INSPECT TESTDATA TALLYING COUNTER FOR ALL 'ABC'
IF COUNTER > 0
DISPLAY 'SKIP! ' TESTDATA
ELSE
DISPLAY 'DONT SKIP ' TESTDATA
END-IF
GOBACK
.
Did you try something like:?
If variable equal to 'abc'
display "skipped"
else
perform process-para
end-if
This would assume variable is defined as PIC XXX or X(3).
If that does not suit, please update your question with a fuller description, some sample input, expected output and what you have tried.
Now it turns out you are looking for 'abc' at a variable location within a piece of data.
There are several ways to do it.
Easiest is
INSPECT field-you-want-to-look-at
TALLYING a-count
FOR ALL value-you-want-to-search-for
a-count can be defined as BINARY PIC 9(4). value-you-want-to-search-for as PIC XXX VALUE 'abc'.
MOVE ZERO TO a-count before the INSPECT.
After the INSPECT you can test a-count which will tell you how many occurences of value-you-want-to-search-for there are in field-you-want-to-look-at.
The reason to use a data-definition (PIC XXX) instead of a literal ('abc') is for ease of maintenance and understanding. There may be more than one place where 'abc' is required in the program, and it may mean the same thing in both places, or one thing in one place and something else in another. With a data-name from the definition you can describe what 'abc' means in each instance. If the value of 'abc' (or one of the 'abc's) needs to change, there is only one place it needs to be changed - in the working-storage.
If (and assuming Enterprise COBOL on the Mainframe due to the COOLGEN reference) you use compiler option OPT(STD) or OPT(FULL) a data-name which is referenced but is never the "target" of anything, ie it has a constant value, is treated as a constant. So you get a named constant as well.
INSPECT FLIGHTPLAN-REFERENCE
TALLYING NO-OF-ENTRIES-TO-EU-AIRSPACE
FOR ALL EU-FLIGHTPLAN-CODE
Is much more easy to understand than
INSPECT VAR1 TALLYING A-COUNT FOR ALL 'abc'