This Christmas I’m going to embrace the past.
COmmon Business Oriented Language (COBOL) 85 standard was the first language I was taught. Napier University was a feeder into the banking and insurance industries in Edinburgh at the time and they had sizeable COBOL farms. It proved profitable too, as a number of students I knew went to the US to alleviate Y2K bugs in thousands of legacy applications. COBOL had fallen out of favour in US colleges.
I was talking to someone the other day who mentioned that he maintains COBOL legacy applications. Apparently things have gone full circle for the UK, where COBOL is no longer taught so his company outsources to India. I’m not overly surprised COBOL is dying out as an educational tool because as languages go its not like much else – Dijkstra certainly didn’t rate it:
The use of COBOL cripples the mind; its teaching should, therefore, be regarded as a criminal offence.
Who am I to argue with the man who killed the goto statement? Certainly as a language it has issues – the 86 standard wasn’t totally compatible with the previous 74 standard; it has no functions; and it has no pointers. Then again it was in part based around the work of Rear Admiral Grace Hopper, the coolest woman ever in computer science, so it couldn’t be all bad could it? She believed that programming languages in mathematical notation were generally not well understood – COBOL is about as self documenting as it gets. It is a product of its time – for example the first six columns are reserved for sequence numbers, this came about as forms were used to write programs which were in turn converted to punch cards.
An example is the best way to explain this, so I decided to revisit Project Euler’s first problem and code it in COBOL, something I haven’t touched in years. As Rear Admiral Grace Hopper said:
It’s easier to ask for forgiveness than it is to get permission.
Of course we need a compiler, Ubuntu has Open COBOL available:
sudo apt-get install open-cobol
Lets consider the problem again:
If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below 1000.
My solution is to iterate through every number from 1 to 1000, test if it divides by 3 or 5 without a remainder and if so then add it to a counter. In pseudocode:
For counter = 1 to 1000 Step 1 If counter % 3 = 0 or number % 5 = 0 Then total = total + counter End If End For Display total
But wait – this is COBOL, a language that doesn’t include the word “terse”. So let’s adapt this to a more verbose piece of pseudocode:
For counter = 1 To 1000 If counter % 3 = 0 Then Set flag To TRUE End If If counter % 5 = 0 Then Set flag To TRUE End If If flag = TRUE Then total = total + counter Set flag To FALSE End If Display total End For
Right, here we go. Its been a long time. COBOL is organised into divisions, sections, paragraphs, sentences and statements – all are terminated by a period. There are four divisions:
- Identification. Unsurprisingly contains information about the program.
- Environment. This isn’t initially obvious but is concerned with portability – this section allows you to isolate anything that is platform specific. So in theory only the environment division needs changing between platforms.
- Data. This holds all the variables and has four sections – file (file handling); linkage (used with sub-routines); report (for report writers); and the one you almost always need, working-storage (where all your working variables are).
- Procedure. Guess what? Yes this is where the program itself is.
IDENTIFICATION DIVISION. PROGRAM-ID. EULER_PROBLEM_ONE.
Not much explanation required there then. We don’t need an environment division, it doesn’t hurt to populate the division name anyway.
The next division requires a little more explanation. We need a working-storage section but COBOL defines variables using level, name, picture clause and optionally a value.
Picture clauses define a variable’s format. Levels allow data to be grouped, for example if you defined a student as having an ID and a name you might have:
01 student. 02 identifier PIC A9(7). 02 name PIC A(30).
Level numbers are for the most part arbitrary as long as the lower the number the higher the level, with the exception of 66 (deprecated), 77 (individual elements) and 88 (used to define conditions). Picture clauses can use: 9 (digit); A (letter); X (digit or letter); V (decimal point); S (sign). You can use A(30) rather than typing loads of A’s.
That just leaves us a procedure division. If you have ever coded assembler then you’ll probably find this easier than if you’ve used C. You need to describe every step. The main iteration construct we need from our pseudocode is a For Loop. COBOL has a PERFORM statement, you need to increment your counter yourself:
PERFORM UNTIL counter = 3 ADD 1 TO counter GIVING counter END-PERFORM.
A general convention is to use upper-case for reserved words and lower-case for variables. Pretty much all operations follow this command’s format – do something (ADD 1) to something (counter) and store it somewhere (counter).
IF condition1 > condition2 THEN STATEMENT ELSE STATEMENT END-IF.
You can nest if statements (there’s no Else If statement). Its not uncommon to find a lot of legacy code that only uses IF statements, why I don’t know because it has a Case statement called EVALUATE.
That just leaves us assignment:
MOVE value TO variable
Putting it all together, you need variables to Hold temporary values used to calculate answers, I always call it junk:
IDENTIFICATION DIVISION. PROGRAM-ID. EULER_PROBLEM_ONE. ENVIRONMENT DIVISION. DATA DIVISION. WORKING-STORAGE SECTION. 01 total PIC 999999 VALUE 0. 01 counter PIC 9999 VALUE 1. 01 junk PIC 999999 VALUE 0. 01 flag PIC 9 VALUE 0. PROCEDURE DIVISION. PERFORM UNTIL counter = 1000 COMPUTE junk = FUNCTION MOD (counter, 3) IF junk = 0 THEN MOVE 1 TO flag END-IF COMPUTE junk = FUNCTION MOD (counter, 5) IF junk = 0 THEN MOVE 1 TO flag END-IF IF flag = 1 THEN ADD counter TO total GIVING total MOVE 0 TO flag END-IF ADD 1 TO counter GIVING counter END-PERFORM. DISPLAY total. STOP RUN.
Remember that indentation is important (despite the code formatting here not wanting to do it) – areas A and B take the first 6 characters so you need to begin in column 7.
Save the file as euler.cob, compile and run it:
cobc -x euler.cob ./euler
Giving the correct answer of 233168, albeit without any frills.
It took me a while to get this to compile – Open COBOL doesn’t care for the AUTHOR statement in the IDENTIFICATION DIVISION so I removed it.
What this snippet of code doesn’t show us is that larger projects are unbelievably verbose, lines and lines of code. Nor does it cover the hilarity of design using Data Flow Diagrams – queuing for hours to collect printouts from the University’s only 132 column printer.
Well that’s been a fun trip down memory lane, I don’t remember why I started this post.
Now get off my lawn.