HP 3000 Manuals

Data Structures [ Getting Started With TRANSACT V ] MPE/iX 5.0 Documentation


Getting Started With TRANSACT V

Chapter 6  Data Structures 

So far, the examples have made little reference to Transact's data
structures.  However, if you go back and scan each example, you will find
that some form of the LIST verb exists in each example.  For many
applications, what you see in the examples is all that is needed.
However, there are times when it is necessary to programatically take
more control over Transact's temporary data storage.

COBOL and Pascal have very well defined data structures.  This chapter
will compare Transact's data structures to those used by both of these
languages.

Typically, high-level application development products have data
structures that are not well defined.  For many applications, this makes
them very easy to work with to generate reports and to update databases
or files.  By the same token, there are applications which become, if not
impossible, then extremely difficult to implement with these products
because of their weak data structures.

Let's start by using some lines from EX40 in Figure 5-12.
____________________________________________
|                                          |
|     1.1   define(item) lastkey i(4):     |
|     1.2                enter i(4),init=0;|
|     1.21  define(item) valid i(4):       |
|     1.22               yes i(4),init=1:  |
|     1.23               no i(4),init=0;   |
|     1.24  list valid:                    |
|     1.25       yes:                      |
|     1.26       no;                       |
|     1.3   list lastkey:                  |
|     1.4        enter;                    |
|     2     list(auto) vorderhead;         |
|     3     list(auto) vorderline;         |
|     3.1   level;                         |
____________________________________________

          Figure 6-1.  Transact data structures 

This same definition in COBOL might be:
_____________________________________________
|                                           |
|      1     01 valid pic 9(4) comp.        |
|      2     01 yes pic 9(4) comp value 1.  |
|      3     01 no pic 9(4) comp value 0.   |
|      4     01 lastkey pic 9(4) comp.      |
|      5     01 enter pic 9(4) comp value 0.|
|      6     01 ws-vorderhead.              |
|      7        02 order-no pic x(8).       |
|      8        02 cust-no pic 9(4).        |
|      9        02 order-status pic x(2).   |
|     10        02 order-date pic x(6).     |
|     11     01 ws-vorderline.              |
|     12        02 line-no pic 9(2).        |
|     13        02 part-number pic x(8).    |
|     14        02 quantity pic 9(6) comp.  |
_____________________________________________

          Figure 6-2.  Comparable COBOL data structures 

And in Pascal:
_______________________________________________________
|                                                     |
|      1     const yes=1;                             |
|      2           no=0;                              |
|      3           enter=0;                           |
|      4     type small_int=-32768..32767;            |
|      5          char2=packed array[1..2] of char;   |
|      6          char4=packed array[1..4] of char;   |
|      7          char6=packed array[1..6] of char;   |
|      8          char8=packed array[1..8] of char;   |
|      9          typ_vorderhead=record               |
|     10                           order_no:char8;    |
|     11                           cust_no:char4;     |
|     12                           order_status:char2;|
|     13                           order_date:char6;  |
|     14                         end;                 |
|     15          typ_vorderline=record               |
|     16                           line_no:char2;     |
|     17                           part_number:char8; |
|     18                           quantity:integer;  |
|     19                         end;                 |
|     20     var valid:small_int;                     |
|     21         lastkey:small_int;                   |
|     22         ws_vorderhead:typ_vorderhead;        |
|     23         ws_vorderline:typ_vorderline;        |
_______________________________________________________

          Figure 6-3.  Comparable PASCAL data structures 

NO and ENTER are reserved words in COBOL. These variable names would have
to be changed, but that is unimportant to our discussion.

The Pascal example is not exactly the same as the Transact or COBOL
example, since Pascal does not have the direct equivalent of an ASCII
numeric data type as COBOL and Transact do.  However, that is not
important.  The important part is being able to compare how storage is
reserved and how access is gained to it.

In Transact, an item is defined one time.  This definition is either done
using a data dictionary or within the program.  In either case, when
space is reserved for the item, the definition part is not included as it
is for COBOL and Pascal.

In the Transact example, DEFINE(ITEM) (lines 1.1 to 1.23) defines the
name, format, and size of five data items.  All five are of single word
integer or binary format.  In addition, whenever storage space is
reserved for enter, yes, or no, the space is initialized to contain the
values 0, 1, or 0, respectively.  Note that DEFINE does not reserve space
for the data items.  It more closely resembles the Pascal TYPE construct.

Lines 1.24 to 1.4 actually reserve space or make the data items known to
Transact.

LIST(AUTO) in lines 2 and 3 also reserves space for data items.  However,
in this case the data items have been previously defined in Dictionary/V.
Also the VPLUS form names vorderhead and vorderline have been defined in
Dictionary/V. These lines are equivalent to the following:
_____________________________________
|                                   |
|     2     list order-no:          |
|     2.1        cust-no:           |
|     2.2        order-status:      |
|     2.3        order-date;        |
|     3     list line-no:           |
|     3.1        part-number:       |
|     3.2        quantity;          |
_____________________________________

          Figure 6-4.  LIST(AUTO) equivalent with LIST 

Later on in the program there are verbs that input data from the VPLUS
screens and other verbs that update data in an IMAGE dataset.  Examples
from the program are:
______________________________________________________
|                                                    |
|      9           get(form) vorderline,fkey=lastkey;|
|     10         put orderline,list=(order-no,       |
|     11                             line-no,        |
|     12                             part-number,    |
|     13                             quantity);      |
______________________________________________________

          Figure 6-5.  VPLUS default with no LIST= 

Line 9 inputs data to the program from the VPLUS screen vorderline.
There is nothing on this line to indicate which data items to retrieve.
In this default case, Transact looks at the form definition in
Dictionary/V to determine which data items are involved.  If we wanted to
be explicit, we could have made this line read:
_____________________________________________________
|                                                   |
|      9     get(form) vorderline,fkey=lastkey      |
|      9.01                      ,list=(line-no,    |
|      9.02                             part-number,|
|      9.03                             quantity);  |
_____________________________________________________

          Figure 6-6.  VPLUS explicit LIST= 

or alternatively:
___________________________________________________
|                                                 |
|      9     get(form) vorderline,fkey=lastkey    |
|      9.01                      ,list=(line-no:  |
|      9.02                             quantity);|
___________________________________________________

          Figure 6-7.  LIST= item range 

to indicate a range of data items.

Lines 10 to 13 add a new record to dataset orderline using data items
order-no, line-no, part-number, quantity.
__________________________________________________
|                                                |
|     10         put orderline,list=(order-no,   |
|     11                             line-no,    |
|     12                             part-number,|
|     13                             quantity);  |
__________________________________________________

          Figure 6-8.  IMAGE explicit item list 

Transact does not automatically figure out which items should be used for
IMAGE. If we omit the LIST= option, Transact assumes that all items
currently known to the program (via LIST) are to be used.  In fact, this
is also true of MPE and KSAM files.

However, the items have to be contiguous in temporary storage if LIST= is
not included.  Since in our example they are not contiguous, we need to
specify LIST=.

COBOL also has the facility to redefine storage.  We have already seen
one example above.  In the COBOL program example, by referencing the
variable ws-vorderhead, a COBOL program would perform a group level
operation on order-no, cust-no, and order-status.  The COBOL program
could also reference each of these data items individually.

Another COBOL example is the following:
____________________________________________
|                                          |
|     1     01 record-data.                |
|     2        02 dte pic 9(6).            |
|     3        02 date-redef redefines dte.|
|     4          04 yy pic 99.             |
|     5          04 mm pic 99.             |
|     6          04 dd pic 99.             |
____________________________________________

          Figure 6-9.  COBOL redefinition of data storage 

This same example in Transact is handled as follows:
___________________________________________
|                                         |
|      1     define(item) date 9(6):      |
|      2                  yy 9(2)=date:   |
|      3                  mm 9(2)=date(3):|
|      4                  dd 9(2)=date(5);|
|      5     list date;                   |
___________________________________________

          Figure 6-10.  Transact redefinition of data storage 

In Transact, these items are referred to as parent and child items.  A
child item as in lines 2 to 4 is equated to a byte offset of the parent
item.  If no offset is specified, it defaults to the start of the parent
item.  Although our program may make a reference to yy, this child item
is never used in the LIST. Space must be reserved for the parent item
which is date.

Lists or arrays are another common data structure.  In COBOL, the
following is a segment of a program containing both a one and two
dimensional array.
___________________________________________________
|                                                 |
|      1     01 empl-table.                       |
|      2       02 empl-rec occurs 10.             |
|      3         04 empl-no pic x(6).             |
|      4         04 empl-name pic x(30).          |
|      5         04 empl-salary pic 9(8)v99 comp. |
|      6                                          |
|      7     01 reg-sales-by-mo.                  |
|      8       02 region-line occurs 10.          |
|      9         04 region pic x(4).              |
|     10         04 month pic 9(8) comp occurs 12.|
___________________________________________________

          Figure 6-11.  COBOL array definitions 

This would be implemented in Transact as follows:
_________________________________________________________________
|                                                               |
|      1     define(item) empl-table 10 x(40):                  |
|      2                    empl-rec x(40)=empl-table:          |
|      3                      empl-no x(6)=empl-rec:            |
|      4                      empl-name x(30)=empl-rec(7):      |
|      5                      empl-salary i(10,2)=empl-rec(37): |
|      6                  reg-sales-by-mo 10 x(52):             |
|      7                    region-line x(52)=reg-sales-by-mo:  |
|      8                      region x(4)=region-line:          |
|      9                      month-data 12 i(8)=region-line(5):|
|     10                        month i(8)=month-data;          |
|     11     list empl-table:                                   |
|     12          reg-sales-by-mo;                              |
_________________________________________________________________

          Figure 6-12.  Comparable Transact array definitions 

Line 1 defines the total number of occurrences (10) and total byte length
of each occurrence (40) of the one-dimensional array.

Line 2 defines one occurrence of employee data to be 40 bytes long.  In a
later section, we will see how to access array items by subscripting or
using the LET(OFFSET) verb.

Lines 3-5 define detail items making up one employee record.  The total
byte length of all these fields adds up to the 40 bytes that makes up one
record.

Line 6 starts a new array definition.

Line 9 defines a second dimension of this array.  The first dimension
holds the data for 10 regions.  The second dimension is made up of 12
months of data for each region.

As we have seen, Transact does have definite data structures which in
many respects correlate quite closely with those in COBOL.

Transact works best when you keep in mind that you only want to add new
information to your data structure, not define anything twice.  For
example, the orders database has several sets which contain the item
part-number.  Two of these sets are inventory and parts.  If a program
needs to have access to all information contained in both of these sets,
it should only list each item one time.  Since part-number is common to
both sets, it should only be listed once.  To illustrate, if a program
first retrieves an inventory record and then needs to retrieve the
corresponding parts record to get the part description, a good way to do
this is:
_______________________________________________________________
|                                                             |
|     1     list part-number:                                 |
|     2          location:                                    |
|     3          quantity:                                    |
|     4          description;                                 |
|     5     get(serial) inventory,list=(part-number:quantity);|
|     6     set(key) list (part-number);                      |
|     7     get parts,list=(description);                     |
_______________________________________________________________

          Figure 6-13.  LISTing items from multiple datasets 

This technique can be used when an item in two or more datasets refers to
the same data.  However, there are times when two datasets or files
contain the same item name, but they are independent of each other.  For
example, the database we have been using for examples has two datasets
that contain an item called quantity.  Even though these data items share
a common name, the meaning is quite different for each set.  In the
inventory set, quantity is the inventory at a particular location.  In
the orderline set, quantity is the quantity ordered on this line of an
order.

Perhaps we need to generate a report which lists all part numbers
ordered, the order quantity, and the total inventory on hand.  The
following program is one way to do this.
__________________________________________________________________________
|                                                                        |
|      1     system ex54,base=orders;                                    |
|      2     define(item) inv-quantity i(6),alias=(quantity(inventory))  |
|      3                  order-quantity i(6),alias=(quantity(orderline))|
|      4                  tot-inv i(6),head="inventory";                 |
|      5     list part-number:                                           |
|      6          inv-quantity:                                          |
|      7          order-quantity:                                        |
|      8          tot-inv;                                               |
|      9     find(serial) orderline,list=(part-number,order-quantity)    |
|     10                            ,perform=100-get-inv;                |
|     11     end;                                                        |
|     12                                                                 |
|     13     100-get-inv:                                                |
|     14                                                                 |
|     15       set(key) list (part-number);                              |
|     16       let (tot-inv) = 0;                                        |
|     17       find(chain) inventory,list=(inv-quantity)                 |
|     18                            ,perform=110-accum-inv;              |
|     19       display(table) part-number:                               |
|     20                      tot-inv:                                   |
|     21                      order-quantity;                            |
|     22       return;                                                   |
|     23                                                                 |
|     24     110-accum-inv:                                              |
|     25                                                                 |
|     26       let (tot-inv) = (tot-inv) + (inv-quantity);               |
|     27       return;                                                   |
__________________________________________________________________________

          Figure 6-14.  Use of ALIAS= for items with same name 

The above program is able to create a unique identifier for each type of
quantity by using the ALIAS= option.  Within the program, the item is
always referenced by the first name in the DEFINE. The ALIAS item name
identifies the item name and dataset as they are known to IMAGE.

Another way to solve this problem is to use the dynamic feature of
Transact's data structures.  The data known to a Transact program can be
redefined or remapped at any time during program execution.  Unlike COBOL
and Pascal, Transact does not map physical data storage at compile time.
Storage is allocated at run time as the program processes LIST verbs.

There are also verbs to deallocate storage when it is no longer needed or
when it needs to be remapped.  These verbs are illustrated in the program
below which solves the same problem as the program shown in Figure 6-14.
________________________________________________________________
|                                                              |
|      1     system ex55,base=orders;                          |
|      4     define(item) tot-inv i(6),head="inventory";       |
|      5     list part-number:                                 |
|      6          quantity:                                    |
|      8          tot-inv;                                     |
|      9     find(serial) orderline,list=(part-number,quantity)|
|     10                            ,perform=100-get-inv;      |
|     11     end;                                              |
|     12                                                       |
|     13     100-get-inv:                                      |
|     14                                                       |
|     14.1     list quantity;                                  |
|     15       set(key) list (part-number);                    |
|     16       let (tot-inv) = 0;                              |
|     17       find(chain) inventory,list=(quantity)           |
|     18                            ,perform=110-accum-inv;    |
|     18.1     set(stack) list (tot-inv);                      |
|     19       display(table) part-number:                     |
|     20                      tot-inv:                         |
|     21                      quantity,head="order-quantity";  |
|     22       return;                                         |
|     23                                                       |
|     24     110-accum-inv:                                    |
|     25                                                       |
|     26       let (tot-inv) = (tot-inv) + (quantity);         |
|     27       return;                                         |
________________________________________________________________

          Figure 6-15.  Use of SET(STACK) LIST 

In this example program, the data storage always exists in one of two
forms.  When lines 15 to 18 and 26 to 27 are executed, the data storage
looks like:
_____________________________________
|                                   |
|      5     part-number            |
|      6     quantity               |
|      8     tot-inv                |
|     14.1   quantity               |
_____________________________________

          Figure 6-16.  LIST register map with same item twice 

During the remainder of the program, the data storage looks like:
_____________________________________
|                                   |
|      5     part-number            |
|      6     quantity               |
|      8     tot-inv                |
_____________________________________

          Figure 6-17.  LIST register after SET(STACK) 

This is all controlled by the repeated execution of lines 14.1 and 18.1.
Line 14.1 reserves space for the item quantity.  It will hold the
inventory quantity at a location while line 17 is executing.  Line 18.1
deallocates this space after we have computed the total inventory
balance.

How does Transact know which quantity we want to use in line 26?  It
doesn't.  Whenever a program instructs Transact to perform some action on
a data item, Transact always uses the most recently defined (LISTed)
version of the item.  Once line 14.1 has been executed, there is no way
we can ever access the space reserved for quantity by lines 5 to 7 until
we execute line 18.1.

In the example above, we could also have written line 18.1 as:
_____________________________________
|                                   |
|     18.1     set(stack) list (*); |
_____________________________________

          Figure 6-18.  Removing the last item name from the LIST 

This deallocates the last data item defined (LISTed) to Transact.

A slightly more general purpose way to implement the last example is to
use marker items.  Just as their name implies, these items mark a data
storage reference point that we can go back to.  Or they can be used in
pairs to delimit a set of data that pertains to a file or dataset.  A
marker is just a name and does not physically take up any data storage
space.

In the last example we can take advantage of both usages of marker items
as follows:
_________________________________________________________________________
|                                                                       |
|      1     system ex59,base=orders;                                   |
|      4     define(item) tot-inv i(6),head="inventory";                |
|      4.1   define(item) begin-orderline @:                            |
|      4.2                end-orderline @;                              |
|      4.3   list tot-inv;                                              |
|      5     list begin-orderline:                                      |
|      5.1        part-number:                                          |
|      6          quantity:                                             |
|      7          end-orderline;                                        |
|      9     find(serial) orderline,list=(begin-orderline:end-orderline)|
|     10                            ,perform=100-get-inv;               |
|     11     end;                                                       |
|     12                                                                |
|     13     100-get-inv:                                               |
|     14                                                                |
|     14.1     list quantity;                                           |
|     15       set(key) list (part-number);                             |
|     16       let (tot-inv) = 0;                                       |
|     17       find(chain) inventory,list=(quantity)                    |
|     18                            ,perform=110-accum-inv;             |
|     18.1     set(stack) list (end-orderline);                         |
|     19       display(table) part-number:                              |
|     20                      tot-inv:                                  |
|     21                      quantity,head="order-quantity";           |
|     22       return;                                                  |
|     23                                                                |
|     24     110-accum-inv:                                             |
|     25                                                                |
|     26       let (tot-inv) = (tot-inv) + (quantity);                  |
|     27       return;                                                  |
_________________________________________________________________________

          Figure 6-19.  Use of marker items 

Lines 4.1 and 4.2 define the marker items.  Lines 5 and 7 put the markers
around the items that we want to use from the orderline set.  Line 9 now
uses the markers to indicate the data item range to be used.  With this
implemented, at some later date we could decide that the program also
needs to retrieve order-no from the orderline set and no changes would
have to be made to line 9.  Order-no would only need be added to the LIST
verb between lines 5 and 7.

In line 18.1, we can now use end-orderline as a reference to reset to and
thus make the section of code that sums the part inventory independent of
the code that retrieves order information.

Transact's dynamic data storage can be illustrated by another example.
However, keep in mind that even though data storage is dynamic, it is not
boundless.  This kind of program is fun to write, but it is not the
example that you want to base your future Transact development on.  In
fact, this example should be viewed simply as demonstration of Transact's
dynamic storage capabilities.
____________________________________________________________
|                                                          |
|      1     system ex60,base=orders;                      |
|      2     define(item) temp-part x(8):                  |
|      3                  from-part x(8):                  |
|      4                  to-part x(8);                    |
|      5     define(item) count i(4),init=0:               |
|      6                  dun x(4),init="no":              |
|      7                  yes x(4),init="yes":             |
|      8                  no x(4),init="no";               |
|      9     define(item) parts-this-pass i(4):            |
|     10                  no-of-parts i(4);                |
|     11     define(item) start-of-parts @;                |
|     12     list temp-part:                               |
|     13          parts-this-pass:                         |
|     14          no-of-parts;                             |
|     15     list count:                                   |
|     16          dun:                                     |
|     17          yes:                                     |
|     18          no;                                      |
|     19     list start-of-parts;                          |
|     20     repeat                                        |
|     21       do                                          |
|     22       list part-number;                           |
|     23       get(serial) parts,list=(part-number),status;|
|     24       if status = 0                               |
|     25         then let (count) = (count) + 1            |
|     26         else let (dun) = (yes);                   |
|     27       doend                                       |
|     28     until (dun) = (yes);                          |
|     29     let (no-of-parts) = (count);                  |
|     30     let (parts-this-pass) = (no-of-parts);        |
|     31     while (parts-this-pass) > 0                   |
|     32       perform 100-bubble-sort;                    |
|     33     set(stack) list (start-of-parts);             |
|     34     let (count) = 0;                              |
|     55     while (count) < (no-of-parts)                 |
|     36       do                                          |
|     37       list part-number;                           |
|     38       let (count) = (count) + 1;                  |
|     39       doend;                                      |
|     40     let (count) = (no-of-parts);                  |
|     41     while (count) > 0                             |
|     42       do                                          |
|     43       display(table) part-number;                 |
|     44       set(stack) list (*);                        |
|     45       let (count) = (count) - 1;                  |
|     46       doend;                                      |
|     47     end;                                          |
|     48                                                   |
____________________________________________________________

          Figure 6-20.  Illustration of dynamic data storage 
______________________________________________________________
|                                                            |
|     49     100-bubble-sort:                                |
|     50                                                     |
|     51       set(stack) list (start-of-parts);             |
|     52       let (count)= 1;                               |
|     53       while (count) < (parts-this-pass)             |
|     54         do                                          |
|     55         let (count) = (count) + 1;                  |
|     56         list from-part:                             |
|     57              to-part;                               |
|     58         if (from-part) < (to-part)                  |
|     59           then                                      |
|     60             do                                      |
|     61             move (temp-part) = (to-part);           |
|     62             move (to-part) = (from-part);           |
|     63             move (from-part) = (temp-part);         |
|     64             doend;                                  |
|     65           set(stack) list (*);                      |
|     66         doend;                                      |
|     67       let (parts-this-pass) = (parts-this-pass) - 1;|
|     68       return;                                       |
______________________________________________________________

          Figure 6-20.  Illustration of dynamic data storage (cont.) 

The above program performs a bubble sort of valid part-numbers.  You will
never want to do this, since it is much easier to let Transact do the
sort for you via the SORT=(part-number) option, but it does illustrate
the dynamics of Transact's data structures.

Lines 20 to 28 serially read the master set that contains part-numbers
and dynamically adds each to the program data storage.  Line 22 reserves
an additional 8 bytes of storage each time it is executed.

Lines 29 to 31 and 49 to 68 perform the bubble sort.  The idea of a
bubble sort is that for each pass over the data, the lowest key item is
floated to the top.  Also, the second pass over the data knows that the
first pass found the lowest value item, so it only has to look at all
data except the one at the top (which is already lowest).

No-of-parts is the count of the total number of items.  Parts-this-pass
is the total number of items that need to be scanned during this pass of
the bubble sort.  Count is just a counter that is reinitialized and
reused as an index through the data storage throughout the program.

Line 30 sets things up for the first pass of the sort.  Line 67 sets
things up for the next pass of the bubble sort after the previous pass
has been completed.

Lines 31 and 32 control the passes of the sort.

Line 51 makes use of a marker to set the data storage pointer to the
start of the part numbers.  Lines 53 to 66 increment through the part
numbers, comparing one entry to the next.  The integral part of this loop
is lines 56, 57, and 65.  Lines 56 and 57 name or remap the next two
parts to be compared.  Line 65 backs the program up one item so that the
next loop will compare the winner of the last compare with the next item.

Lines 58 to 64 perform the compare of the next two parts and when
completed, the lowest value part-number has been floated up.  It is then
compared to the next item during the next loop.

After the bubble sort is completed, lines 33 to 39 remap the data storage
to just contain the name part-number.  This needs to be done because the
bubble sort routine kept calling items from-part and to-part.

Lines 40 to 46 start at the top and print out the value of each
part-number in ascending order.


MPE/iX 5.0 Documentation