REBOL for COBOL programmers

Go to table of contents Go to feedback page

SEARCH

Date written: February 14, 2014
Date revised:
Date reviewed:

This page explains some REBOL equivalents of SEARCH.


COBOL SEARCH

the SEARCH verb is used for searching tables in memory. It looks something like this. There are variations, but you probably know them and they are not important at this time.

SEARCH identifier-1
    AT END imperative-statement-1
    WHEN condition-1 imperative-statement-2

Because COBOL was used in the days of massive data files, and SEARCH is for searching in memory, one does not normally see SEARCH applied to data files. Usually it applied to small tables, like tables of code numbers and their descriptions, tables of rates, postal codes and states, stuff like that. Of course the question comes up of, how does that data get into memory in the first place. It could be hard-coded into a program, but that is not the best for maintainenance, so it probably would be read into memory from a file. So why not just use a file for the table? Actually, that probably is the best idea, but there are cases where it might make sense to do things differently. In unix, it is popular to use text files of configuration data that are read when a program starts up. Something like that would be a good candidate for being loaded into memory and then searched. How would that get into memory? In the COBOL world one would write code to load it, but let's not get too far into that at this time because it is not important for now. Let's set up a simple example.

01  RATE-CODE-TABLE.
    03  RATE-CODE-ENTRY
        OCCURS 100 TIMES
        INDEXED BY RATE-CODE-IX.
        05  RATE-CODE             PIC X(4).
	05  RATE-DESC             PIC X(20).
        05  RATE-EFF-DATE         PIC X(8).
        05  RATE-VALUE            PIC S9(5)V99
77  RATE-SEARCH-CODE              PIC X(4).
77  RATE-FOUND                    PIC X.
...
MOVE (some value obtained from data somewhere) TO RATE-SEARCH-CODE.
PERFORM RATE-LOOKUP.
IF RATE-FOUND = "T"
    MOVE RATE-CODE-DESC (RATE-CODE-IX) TO (somewhere).
    etc., etc.
IF RATE-FOUND = "F"
    (do some appropriate error handling). 
...
RATE-LOOKUP.
    SET RATE-CODE-IX TO 1.
    SEARCH RATE-CODE-ENTRY
        AT END
            MOVE "F" TO RATE-FOUND
        WHEN RATE-CODE (RATE-CODE-IX) = RATE-SEARCH-CODE
            MOVE "T" TO RATE-FOUND.
* If the search was successful, RATE-CODE-IX points to the entry
* where the match was found.

Notice how the search works. You search the item that OCCURS so many times, and when the WHEN condition is satisfied, you do something. In this example, for simplicity, we just set a flag, but you could do anything.

The REBOL equivalent

Things are a bit different in the REBOL world. For one thing, REBOL is for "programming in the small," so it is more common for data files to be kept entirely in memory. Since data files are, basically, one thing (record) after another, REBOL has lots of functions for dealing with one thing after another, and special datatypes for one thing after another, specifically, the various "series" datatypes (string, block). So rather than get into an explanation of all the things that can be done with a series, let's pick just one thing that can be used to get results similar to the COBOL SEARCH.

In thinking about the COBOL SEARCH, the question came up of how that data got into memory in the first place. The example above did not account for that. In the example above, RATE-CODE-TABLE might have redefined a constant area where rates were hard-coded. A better idea would have been to have those rates in a file and read them in at the start of the program. The data file could be a fixed format, in which case each record would just be put into one table entry. If the data were not in a fixed format, the code to load it would have had to parse it in some way.

In REBOL, we have that code-is-data/data-is-code philosophy, and the idea that REBOL is its own meta-language. That creates some interesting opportunities in this area of tables of data. If you wanted to take the design approach that a rate table like our example is kept in a text file that is read at the start, you could write the table in a text file (let's say RateTable.txt) in a form like this:

"1235" ["Monthly" 01-JAN-2014 $12.00]
"1240" ["Weekly"  01-JAN-2014 $3.00]
"1245" ["Biweekly" 01-JAN-2014 $6.00]
"1250" ["Quarterly" 01-JAN-2014 $48.00]
"1255" ["Annually" 01-JAN-2014 $100.00]

Notice that all those values are recognized by REBOL. Notice also that the brackets around some of the items are a REBOL feature. This data is understandable by REBOL as it is. It describes itself. This data can be brought into memory, parsed, and assigned to a word, with one line of REBOL code:

RATE-CODE-TABLE: load %RateTable.txt

When you perform the above function, RATE-CODE-TABLE is a block. It is a series of ten items, one after the other. The first item is "1235." The second item is ["Monthly" 01-JAN-2014 $12.00]. And so on. There is a REBOL function that operates on a series and can be use very handily on this series to search it for those string items at the front of each record. That is the SELECT function.

The SELECT function searches a series for a specific item, and, if it finds that item, returns, as a function return value, the NEXT item in the series. That is exactly what we want to do. If we want to search for "1245" we want to get our hands on ["Biweekly" 01-JAN-2014 $6.00]. SELECT will return that block as a result. We can use that result as it is, or, to maybe make the code clearer, we can assign a word to that result and refer to that word. In addition, if the item we search for is not found, the SELECT returns NONE, which, if we test it with "if," comes out as "false." Following our above example (using the REBOL console):

>> RATE-CODE-TABLE: load %RateTable.txt
== ["1235" ["Monthly" 1-Jan-2014 $12.00]
    "1240" ["Weekly" 1-Jan-2014 $3.00]
    "1245" ["Biweekly" 1-Jan-2014 $6.00]
    "12...
>> length? RATE-CODE-TABLE
== 10
>> PROBE FIRST RATE-CODE-TABLE
"1235"
== "1235"
>> PROBE SECOND RATE-CODE-TABLE
["Monthly" 1-Jan-2014 $12.00]
== ["Monthly" 1-Jan-2014 $12.00]
>> RATE-SEARCH-CODE: "1240"
== "1240"
>> RATE-FOUND: SELECT RATE-CODE-TABLE "XXXX"
== none
>> PROBE RATE-FOUND
none
== none
>> either RATE-FOUND [print "rate found"] [print "rate not found"]
rate not found
>> RATE-FOUND: select RATE-CODE-TABLE RATE-SEARCH-CODE
== ["Weekly" 1-Jan-2014 $3.00]
>> type? RATE-FOUND
== block!
>> length? RATE-FOUND
== 3
>> probe RATE-FOUND/1
"Weekly"
== "Weekly"
>> probe RATE-FOUND/2
1-Jan-2014
== 1-Jan-2014
>> probe RATE-FOUND/3
$3.00
== $3.00
>> (escape)
>> either RATE-FOUND [print "rate found"] [print "rate not found"]
rate found

A lot of examples there, but look at the one key line:

RATE-FOUND: select RATE-CODE-TABLE RATE-SEARCH-CODE
One line of code searches the "table" and returns what you are hoping to find. In COBOL, you would have access to the data items defined in the table, RATE-DESC(RATE-CODE-IX), RATE-VALUE(RATE-CODE-IX), etc. while in REBOL you would have to be content with RATE-FOUND/1, RATE-FOUND/3, etc., but all in all, a pretty smooth way to search in a scenario of this type.