JES: Just Educational Services

Jonathan E. Sisk's
Pick/BASIC: A Programmer's Guide

Chapter 13 - Manipulating Dynamic Arrays

Click to enlarge

Program example 11 illustrates more variations on handling dynamic arrays. The functions introduced in this example include the ON-GOSUB statement the INSERT function, the REPLACE function, the DELETE function, and the powerful LOCATE statement.

The basic premise of Example 11 is that it permits the user to build and display a dynamic array. This is an example of processing an item before writing it to a file. The main difference between this and a practical example is that, in this example, the positions in which you place data are generalized. In a real-world program, each position is predetermined.

Enter Program 11 from the example shown in Fig. 13-1.

Fig, 13-1. Program Example 11.

EX.011
001 * EX.011
002 * HANDLING ARRAYS AND LOCAL SUBROUTINES
003 * mm/dd/yy: date last modified
004 * JES: author's initials
005 *
006 PROMPT ":"
007 *
008 EQU TRUE TO 1
009 EQU FALSE TO 0
010 WORK.ARRAY = "" ; * INITIALIZE 'DYNAMIC' ARRAY
011 *
012 10 * FORMAT SCREEN
013 *
014 LOOP
015 PRINT @(-1): @(20,0): "HANDLING ARRAYS":
016 PRINT @(3,2) : "WORK ARRAY:": WORK.ARRAY:
017 PRINT @(3,5) : "HERE ARE THE AVAILABLE MENU OPTIONS :" :
018 PRINT @(3,7) : "I --> INSERT STRING INTO ARRAY" :
019 PRINT @(3,8) : "R --> REPLACE STRING IN ARRAY":
020 PRINT @(3,9) : "D --> DELETE STRING FROM ARRAY" :
021 PRINT @(3,10): "L --> LOCATE (ATTRIBUTE) STRING IN ARRAY":
022 PRINT @(3,11) : "C --> CLEAR ARRAY AND START OVER" :
023 PRINT @(3,13) : "ENTER OPTION OR 'QUIT' ":
024 INPUT OPTION
025 UNTIL OPTION # "" DO REPEAT
026 *
027 * EVALUATE RESPONSE TO OPTION REQUEST
028 *
029 IF OPTION = "QUIT" THEN STOP
030 ON INDEX("IRDLC",OPTION,1) GOSUB 100,200,300,400,500
031 *
032 * RETURN TO TOP OF PROGRAM
033 *
034 GOTO 10
035 *
036 100 * INSERT STRING
037 *
038 PRINT @(3,16): "ENTER STRING TO INSERT" :
039 INPUT STRING
040 IF STRING = "QUIT" THEN STOP
041 GOSUB 700; * GET AMC NUMBER
042 GOSUB 800; * GET VMC NUMBER
043 GOSUB 900; * GET SVMC NUMBER
044 WORK.ARRAY = INSERT(WORK.ARRAY,AMC,VMC,SVMC,STRING)
045 RETURN
046 *
047 200 * REPLACE STRING
048 *
049 PRINT @(3,16): "ENTER STRING TO USE IN REPLACE" :
050 INPUT STRING
051 IF STRING = "QUIT" THEN STOP
052 GOSUB 700; * GET AMC NUMBER
053 GOSUB 800; * GET VMC NUMBER
054 GOSUB 900; * GET SVMC NUMBER
055 WORK.ARRAY = REPLACE(WORK.ARRAY,AMC,VMC,SVMC,STRING)
056 RETURN
057 *
058 300 * DELETE STRING
059 *
060 PRINT @(3,16) : "DELETE ELEMENT FROM ARRAY..."
061 GOSUB 700; * GET AMC NUMBER
062 GOSUB 800; * GET VMC NUMBER
063 GOSUB 900; * GET SVMC NUMBER
064 WORK.ARRAY = DELETE(WORK.ARRAY,AMC,VMC,SVMC)
065 RETURN
066 *
067 400 * LOCATE STRING
068 *
069 LOOP
070 PRINT @(3,15) : "ENTER STRING TO LOCATE" :
071 INPUT STRING
072 UNTIL STRING # "" DO REPEAT
073 IF STRING = "QUIT" THEN STOP
074 *
075 LOOP
076 PRINT @(3,16): "(A)TTRIBUTE, (V)ALUE OR (S) UB-VALUE ?":
077 INPUT TYPE
078 UNTIL TYPE # "" DO REPEAT
079 IF TYPE = "QUIT" THEN STOP
080 *
081 * NOW USE APPROPRIATE LOCATE STATEMENT
082 *
083 BEGIN CASE
084 CASE TYPE = "A"
085 LOCATE(STRING,WORK.ARRAY;AMC.POSITION) THEN
086 FOUND = TRUE
087 END ELSE
088 FOUND = FALSE
089 END
090 VMC.POSITION = 0
091 SVMC.POSITION = 0
092 CASE TYPE = "V"
093 GOSUB 700 ; * GET AMC TO SEARCH FOR VALUE
094 LOCATE(STRING,WORK.ARRAY,AMC;VMC.POSITION) THEN
095 FOUND = TRUE
096 END ELSE
097 FOUND = FALSE
098 END
099 SVMC.POSITION = 0
100 CASE TYPE = "S"
101 GOSUB 700 ; * GET AMC TO SEARCH FOR VALUE
102 GOSUB 800 ; * GET VMC TO SEARCH FOR SUB- VALUE
103 LOCATE(STRING,WORK.ARRAY,AMC,VMC;SVMC.POSITION) THEN
104 FOUND = TRUE
105 END ELSE
106 FOUND = FALSE
107 END
108 CASE 1
109 PRINT @(3,18) : "THAT WASN'T A VALID OPTION"
110 GOSUB 1000 ; * WAIT FOR RESPONSE
111 END CASE
112 *
113 IF FOUND THEN
114 PRINT @(3,18) : @(-3) ; * CLEAR TO END OF SCREEN
115 PRINT @(3,18) : STRING : "WAS FOUND. "
116 PRINT @(3,19) : "ATTRIBUTE": @(15,19) : AMC.POSITION
117 PRINT @(3,20): "VALUE": @(15,20): VMC.POSITION
118 PRINT @(3,21): "SUB-VALUE": @(15,21): SVMC.POSITION
119 END ELSE
120 PRINT @(3,18) : @(-3) : STRING: "NOT FOUND"
121 END
122 *
123 GOSUB 1000 ; * "PAUSE..."
124 RETURN
125 *
126 500 * CLEAR ARRAY
127 *
128 PRINT @(3,16): "ARRAY CLEARED"
129 WORK.ARRAY = ""
130 GOSUB 1000 ; * "PAUSE..."
131 RETURN
132 *
133 700 * GET AMC NUMBER
134 *
135 LOOP
136 PRINT @(3,18) : "ENTER ATTRIBUTE NUMBER":
137 INPUT AMC
138 UNTIL NUM(AMC) OR AMC = "QUIT" DO REPEAT
139 IF AMC = "QUIT" THEN STOP
140 IF AMC = "" THEN AMC = 0 ; * FORCE NULL TO ZERO
141 RETURN
142 *
143 800 * GET VMC NUMBER
144 *
145 LOOP
146 PRINT @(3,19) : "ENTER VALUE NUMBER" :
147 INPUT VMC
148 UNTIL NUM(VMC) OR VMC = "QUIT" DO REPEAT
149 IF VMC = "QUIT" THEN STOP
150 IF VMC = "" THEN VMC = 0 ; * FORCE NULL TO ZERO
151 RETURN
152 *
153 900 * GET SVMC NUMBER
154 *
155 LOOP
156 PRINT @(3,20): "ENTER SUB-VALUE NUMBER" :
157 INPUT SVMC
158 UNTIL NUM(SVMC) OR SVMC = "QUIT" DO REPEAT
159 IF SVMC = "QUIT" THEN STOP
160 IF SVMC = "" THEN SVMC = 0 ; * FORCE NULL TO ZERO
161 RETURN
162 *
163 1000 * PAUSE AND AWAIT RESPONSE
164 *
165 PRINT @(3,22) : "PRESS <CR> TO CONTINUE" :
166 INPUT RESPONSE
167 IF RESPONSE = "QUIT" THEN STOP
168 RETURN
169 *
170 * ALL DONE
171 *
172 END

THE GOSUB STATEMENT AND LOCAL SUBROUTINES

The GOSUB statement is used in PICK/BASIC programs to transfer execution to a section of code called a local subroutine. Often this section of code performs a series of instructions that need to be repeated many times. This is more efficient, obviously, than coding the same series of instructions over and over in a program.

030 ON INDEX("IRDLC",OPTION,1) GOSUB 100,200,300,400,500

The reason that it is called a local subroutine is that the program instructions that comprise the subroutine are physically contained in the same program, and consequently, any program variables may be shared. Subroutines normally are gathered together near the physical end of the program. Local subroutines are the opposite of external subroutines, in which the program instructions are physically located in a separate program (item), with its own unique program name (item-id). In external subroutines, shared variables are specifically "passed into" the subroutine when it is activated.

The GOSUB has the same general format as the GOTO statement discussed in Example 2. This general format is:

GOSUB statement.label

The local subroutines are often gathered together near the bottom of a program. In most versions of the Pick System, the statement.label must be a number; some versions now also allow alphanumeric statement labels.

When the GOSUB statement is executed, execution immediately transfers to the line number that begins with the statement label. Any number of statements may be executed in the subroutine. The subroutine must have a RETURN statement as the last executable statement in the routine. When the RETURN statement is executed, execution returns to the next line after the line on which the GOSUB statement was found.

Study the general example of using the GOSUB statement shown in Fig. 13-2. On line one, the program immediately transfers execution to local subroutine 1000, which is found on line 5 of the program. On line 7, the current system time is displayed in external format. Upon executing line 8, execution returns to line 2, the first line after the GOSUB 1000 statement.

On line 2, execution passes immediately to local subroutine 2000, which occurs on line 10 of the program. At line 12, the current system date is displayed in external format. Line 13 returns execution back to line 3, where the program immediately stops.


001 GOSUB 1000; * transfer control to subroutine 1000
002 GOSUB 2000; * transfer control to subroutine 2000
003 STOP ; * terminate program execution
004 *
005 1000 * DISPLAY THE CURRENT TIME
006 *
007 PRINT "THE CURRENT TIME IS": OCONV(TIME(),"MTHS")
008 RETURN ; * all done. return to next statement
009 *
010 2000 * DISPLAY THE CURRENT DATE
011 *
012 PRINT "THE CURRENT DATE IS": OCONV(DATE() ,"D2-")
013 RETURN
014 *
Fig. 13-2. An example of the GOSUB statement.

THE ON-GOSUB STATEMENT

Line 30 introduces the ON-GOSUB statement, which is a computed GOSUB statement. This means that the actual GOSUB is selected based on the value of the INDEX expression:

030 ON INDEX("IRDLC",OPTION,1) GOSUB 100,200,300,400,500

Suppose there were five menu choices in a program, each of which performs a separate local routine within a program. This may be coded as shown in Fig. 13-3. In lines 3 through 7, each possible response to the question is individually tested to determine which subroutine will be activated. This is exactly what Program Example 11 did. There are five possible responses to the question.

The ON-GOSUB statement has the general format:

ON numeric.expression GOSUB statement.label,statement.label ...

The value of numeric. expression determines which statement.label program execution is transferred to upon evaluation.

For example, examine the use of a calculated GOSUB statement in Fig. 13-4. If you enter the number "3," the statement at line 8 is executed. The message "SUBROUTINE THREE" is displayed. Program execution then immediately returns to line 4, where the program stops.

The nature of the problem here is how to turn the alphabetic menu choices into numbers. Enter the INDEX function, stage left. The INDEX function, as discussed earlier in Example 8, is used to return the numeric position of a string within another string. Observe line 30 one more time:

030 ON INDEX("IRDLC",OPTION,1) GOSUB 100,200,300,400,500


001 PRINT "ENTER OPTION (A,B,C,D OR E) " :
002 INPUT OPTION
003 IF OPTION = "A" THEN GOSUB 100
004 IF OPTION = "B" THEN GOSUB 200
005 IF OPTION = "C" THEN GOSUB 300
006 IF OPTION = "D" THEN GOSUB 400
007 IF OPTION = "E" THEN GOSUB 500
008 STOP
009 *
010 100 * "A" ROUTINE
011 *
013 RETURN
014 *
015 200 * "B" ROUTINE
016 *
017 RETURN
018 *
019 300 * "C" ROUTINE
020 *
021 RETURN
022 *
023 400 * "D" ROUTINE
024 *
025 RETURN
026 *
027 500 * "E" ROUTINE
028 *
029 RETURN

Fig. 13-3. "Fall-through" logic using GOSUB statements.


The embedded INDEX function searches for the first occurrence of the response you entered into OPTION within the string of characters, "IRDLC." If you entered the letter "R, " for example, the INDEX function returns the number 2 to the ON-GOSUB statement, which then transfers execution to local subroutine 200, the second statement label in the list of labels.


001 PRINT "ENTER A NUMBER BETWEEN 1 AND 5"'
002 INPUT RESPONSE
003 ON RESPONSE GOSUB 1000,2000,3000,4000,5000
004 STOP
005 *
006 1000 PRINT "SUBROUTINE ONE" ; RETURN
007 2000 PRINT "SUBROUTINE TWO" ; RETURN
008 3000 PRINT "SUBROUTINE THREE" ; RETURN
009 4000 PRINT "SUBROUTINE FOUR" ; RETURN
010 5000 PRINT "SUBROUTINE FIVE" ; RETURN

Fig. 13-4. Calculated GOSUB statements.


ABOUT THE INSERT FUNCTION

The INSERT function, briefly introduced in Example 10, is used to place a string at a specific location in a dynamic array. If the necessary delimiters are not in place to accommodate your request, then the INSERT function automatically puts them in to ensure that the string ends up in the position you requested.

The general form of the INSERT function is:

array.variable = INSERT(array.variable, amc.expression,...
... vmc.expression, svmc.expression, string.expression)

Note that the "..." ellipsis in the above general format--and throughout this text--means that the statement has been broken up into multiple lines for explanation purposes only. The ellipsis is not part of the syntax.

This function requires all five of the arguments within the parentheses and always appears on the right side of an equals operator, meaning that it is always the source of an assignment.

The array.variable on the left side of the "=" assignment operator is the same as the array.variable on the right side of the operator. The amc.expression is an expression which contains a number indicating the attribute position where the string is to be placed. The vmc.expression is an expression which contains a number indicating the value position within the specified attribute. The svmc.expression is an expression which contains a number indicating the subvalue position within a specified value.

The string.expression is the expression which contains the string of characters to be inserted at some specific location within the dynamic array. Any statement or function which produces output may be used here. Note that the special argument "-1" may be used interchangeably with any of the numeric expressions within the list of arguments to append the string to the end of the item, the end of an attribute, or to the end of a value, as illustrated in Example 10.

The "Shortcut" Method of INSERTing

Some implementations of Pick allow a shortcut method, which effectively allows certain unnecessary arguments to be omitted. The syntax changes a bit, to the general forms listed in the next two sections.

Omitting the VMC and SVMC Expressions.

This form may be used when you simply want to insert a new attribute at some location within a dynamic array:

array.variable = INSERT(array.variable, amc.expression; string.expression)

Note the first appearance of a semicolon (;) between the amc.expression and the string.expression being inserted. On machines that allow the shortcut method, this means that the expression following the semicolon is the actual string to be inserted into the attribute specified by the amc.expression.

Omitting the SVMC Expression.

This form may be used to insert a new value within a particular attribute:

array.variable = INSERT(array.variable, amc.expression ...
... vmc.expression; string.expression)

Once again the semicolon appears in the syntax, indicating that the argument following is the string expression which is to be inserted into the array at the specified attribute and value locations.

The INSERT Function in Context

On lines 38 and 39 of the example program (Fig. 13-5) you are prompted to enter the string of characters you want to insert. The response you provide is stored in the variable STRING. After checking to see if you entered "QUIT," the program transfers execution to local subroutine 700 (Fig. 13-6), which is used to input a number--which is, in turn, used as the attribute position.

At line 138 the loop begins. On lines 139 and 140 you are requested to enter the attribute number, which is subsequently stored in the variable, AMC. Line 141 checks to make sure that the response you entered was either a numeric value or the word "QUIT." Line 142 deals with your response to quit, or falls through if you choose not to quit.

Line 143 exists as a protective mechanism. Some implementations of Pick treat the null string as though it were numeric, meaning that it passes through the NUM function as true. Unfortunately, the INSERT function is not quite as sympathetic with null strings. If the INSERT function tries to insert a string into a location indicated by a null string, it often will report the message, "NON-NUMERIC DATA WHERE NUMERIC DATA REQUIRED!"

Line 143 checks the contents of AMC to determine if it is null. If it is null, then AMC is assigned the value 0 (zero). On line 144 the RETURN statement is issued. This causes program execution to return to line 42, where the statement GOSUB 800 is issued. This again sends execution off to another local subroutine (Fig. 13-7).

The logic is identical to that of local subroutine 700, which requested the AMC numeric value. Coincidentally, this is also the same logic for local subroutine 900, which requests the subvalue mark count, SVMC.


036 100 * INSERT STRING
037 *
038 PRINT @(3,16) : "ENTER STRING TO INSERT" :
039 INPUT STRING
040 IF STRING = "QUIT" THEN STOP
041 GOSUB 700 ; * GET AMC NUMBER
042 GOSUB 800 ; * GET VMC NUMBER
043 GOSUB 900 ; * GET SVMC NUMBER
044 WORK.ARRAY = INSERT(WORK.ARRAY,AMC,VMC,SVMC,STRING)

Fig 13-5 Use of the INSERT function in Program Example 11.



133 700 * GET AMC NUMBER 134 *
135 LOOP
136 PRINT @(3,18) : "ENTER ATTRIBUTE NUMBER" :
137 INPUT AMC
138 UNTIL NUM(AMC) OR AMC = "QUIT" DO REPEAT
139 IF AMC = "QUIT" THEN STOP
140 IF AMC = "" THEN AMC = 0 ; * FORCE NULL TO ZERO
141 RETURN

Fig. 13-6. The logic behind local subroutine 700.


Once subroutines 700, 800, and 900 have completed their tasks, execution returns to line 44, where the following INSERT function is performed on WORK.ARRAY:

044 WORK.ARRAY = INSERT(WORK.ARRAY,AMC,VMC,SVMC,STRING)

After execution of line 44, the RETURN statement transfers execution back to line 31, the first line after the GOSUB statement that initially transferred execution to subroutine 100. Since there is no executable code on line 31, the next line which causes the program to do anything is line 34, which executes the statement "GOTO 10." This unconditionally transfers execution back to the starting point of the program, where the menu of choices is displayed and WORK.ARRAY is displayed.

ABOUT THE REPLACE FUNCTION

The REPLACE function was also briefly introduced in Example 10. It is typically used to exchange the contents of a specific location within a dynamic array. Like the INSERT function, it too may add the appropriate delimiters to accommodate your request.

This is the general form of the REPLACE function:

array.variable = REPLACE(array.variable, amc.expression, ...
... vmc.expression, svmc.expression, string.expression)


143 800 * GET VMC NUMBER
144 *
145 LOOP
146 PRINT @(3,19) : "ENTER VALUE NUMBER" '
147 INPUT
148 UNTIL NUM(VMC) OR VMC = "QUIT" DO REPEAT
149 IF VMC = "QUIT" THEN STOP
150 IF VMC = "" THEN VMC = 0 ; * FORCE NULL TO ZERO
151 RETURN

Fig. 13-7. The logic behind local subroutine 800.


The REPLACE function, like the INSERT function, requires all five of the arguments within the parentheses; some versions of Pick do allow the "shortcut" method discussed earlier in this chapter.

The array.variable on the left side of the "=" assignment operator is the same as the array.variable on the right side of the sign. The amc.expression is an expression which derives a number indicating the attribute position where the string is to be placed. The vmc.expression is an expression which derives a number indicating the value position within the specified attribute. The svmc.expression is an expression which contains a number indicating the subvalue position within a specified value.

The string.expression is the expression which contains the string of characters to be replaced at some specific location within the dynamic array. Any statement or function which produces output may be used here. Note that the special argument, "-1", may be used interchangeably with any of the numeric expressions within the list of arguments to append the string to the end of the item, the end of an attribute, or to the end of a value, as illustrated in Example 10.

The "Shortcut" Method of Replacement

Some implementations of Pick allow a "shortcut" method, which effectively allows certain unnecessary arguments to be omitted. The syntax changes a bit, to the following general forms.

Omitting the VMC and SVMC Expressions.

This form may be used when you simply need to replace the contents of an entire attribute at some location within a dynamic array:

array.variable = REPLACE(array.variable, amc.expression; string.expression)

Note the appearance of a semicolon between the amc.expression and the string.expression being replaced. On machines that allow the shortcut method, this means that the expression following the semicolon is the actual string to be replaced in the attribute specified by the amc.expression.

Omitting the SVMC Expression.

This form may be used to replace a value within a particular attribute:

array.variable = REPLACE(array.variable, amc.expression, ...
... vmc.expression; string.expression)

Once again the semicolon appears in the syntax, indicating that the following argument is the string expression which is to be replaced in the array at the specified attribute and value locations.

The REPLACE Function in Context

On lines 49 and 50 of Example 11 (Fig. 13-8), you are asked to enter the string of characters you want to use to replace an existing string. The response that you provide is stored in the variable STRING. After checking to see if you entered "QUIT," the program transfers execution to local subroutines 700, 800, and 900 to request AMC, VMC, and SVMC, respectively.


047 200 * REPLACE STRING
048 *
049 PRINT @(3,16) : "ENTER STRING TO USE IN REPLACE" :
050 INPUT STRING
051 IF STRING = "QUIT" THEN STOP
052 GOSUB 700 ; * GET AMC NUMBER
053 GOSUB 800 ; * GET VMC NUMBER
054 GOSUB 900 ; * GET SVMC NUMBER
055 WORK.ARRAY = REPLACE(WORK.ARRAY,AMC,VMC,SVMC,STRING)

Fig. 13-8. Use of the REPLACE function in Program Example 11.


Line 55 performs the REPLACE function. After replacing the new string into the array, execution is then returned to line 34, where the GOTO 10 statement is executed, causing execution to transfer to the top of the program.

THE DELETE FUNCTION

The DELETE function is used to remove an attribute, value, or subvalue from a dynamic array. In deleting a location, the corresponding delimiters that accompanied the contents of the location are also deleted. This means, for example, that if you delete attribute two from an array, all of the following attributes effectively move "up" by one position. If you simply want to "null out" a location in an array, this may be done with the REPLACE function, which does not remove the delimiters.

Here is the general form of the DELETE function:

array.variable = DELETE(array.variable, amc.expression ...
... vmc.expression, svmc.expression)

Unlike the INSERT and REPLACE functions mentioned earlier, this function requires only four arguments. The contents of the string being deleted do not have to be known, meaning that you may delete a string from within the array simply by indicating the element location you wish deleted.

The DELETE function produces a string. It is loaded, or stored, in the variable on the left side of the assignment operator. Therefore, the array.variable on the left side of the "=" generally (but not always) is the same as the array.variable on the right side. The amc.expression is an expression which contains a number indicating the attribute position to be deleted. The vmc.expression is an expression which contains a number indicating the value position within the specified attribute. The svmc.expression is an expression which contains a number indicating the subvalue position within a specified value.

Note that the special argument "-1" may not be used in this function. Also, the shortcut method is not available. This means that each argument must be provided in the expression.

Now let's look at the DELETE function as it is used in Example 11:


058 300 * DELETE STRING
059 *
060 PRINT @(3,16) : "DELETE ELEMENT FROM ARRAY..."
061 GOSUB 700 ; * GET AMC NUMBER
062 GOSUB 800 ; * GET VMC NUMBER
063 GOSUB 900 ; * GET SVMC NUMBER
064 WORK.ARRAY = DELETE(WORK.ARRAY,AMC,VMC,SVMC)

In subroutine 300, execution is sent to local subroutines 700, 800, and 900 to request the AMC, VMC, and SVMC. These three variables are then used in the DELETE function on line 64. After renaming execution to the top of the program, the array reflects the change made by the DELETE function.

HOW TO TEST THE PROGRAM

By this point, you have probably experimented with Example 11. Before covering the LOCATE statement, take a moment to perform the following steps, which will make it easier to explain and understand this function.

1) Clear the Work Array. An option is provided on the menu to clear, or initialize, the variable WORK.ARRAY. This is accomplished by choosing the letter C from the menu. Choosing this option causes execution to be transferred to local subroutine 500:


126 500 * CLEAR ARRAY
127 *
128 PRINT @(3,16) : "ARRAY CLEARED"
129 WORK.ARRAY = ""
130 GOSUB 1000 ; * "PAUSE..."
131 RETURN

Line 128 simply announces the fact that the array has been cleared. Line 129 assigns a null to the variable WORK.ARRAY, instantly replacing its former contents. Line 130 sends execution to local subroutine 1000, which simply pauses the program and awaits a response. After the response is received, execution returns to the top of the program.

2) Choose the I Option. This is how you elect to INSERT a string into the dynamic array.

3) Enter FRED at the prompt for you to enter the string.

4) Enter the attribute, value, and subvalue. At the appropriate prompt, enter these numbers, which are 1, 1, and 0, respectively. Upon displaying the work array again, the string FRED appears. This is the first value of the first attribute.

5) Choose the I Option. This begins the process of inserting a second string. 6) Enter BARNEY at the prompt for you to enter the string.

6) Enter the attribute, value, and subvalue. At the appropriate prompt, enter 1, 2, and 0, respectively. Upon displaying the work array again, the string BARNEY appears in the second value of the first attribute.

7) Enter the Remaining Data. Following the same steps as above, enter:

WILMA
Attribute: 1
Value: 2
Subvalue: 0
BETTY
Attribute: l
Value: -1
Subvalue: 0
DINO
Attribute: 2
Value: 0
Subvalue: 0

Note that when you displayed the work array after WILMA was inserted into the second value of the first attribute, BARNEY was "pushed down" into the third value and the work array appeared as:

FRED]WILMA]BARNEY

Then the string BETTY appeared as the fourth, or last, value in attribute one. Finally the string DINO appeared as the first (and only) value in attribute two.

The work array now appears as:

FRED]WILMA]BARNEY]BETTY^DINO

and you are now ready to meet the LOCATE statement. Fasten your seat belts. This may be a rough ride.

THE LOCATE STATEMENT

The LOCATE statement searches for strings within a dynamic array. The string it searches for may represent an entire attribute, value, or subvalue, and the statement indicates if the string was located. The LOCATE statement has several general forms, depending on where and how it is being applied.

For example, to retrieve an attribute requires the following general form:

LOCATE(string.expression,array.variable;setting. variable)...
... {THEN statement(s)} ELSE statement(s)

This form is illustrated in Example 11 in the following section of code:


084 CASE TYPE = "A"
085 LOCATE(STRING,WORK.ARRAY;AMC.POSITION) THEN
086 FOUND = TRUE
087 END ELSE
088 FOUND = FALSE
089 END

In the CASE construct, responding with the letter A indicates that you are attempting to locate an attribute; this CASE statement evaluates true and the LOCATE statement on line 85 is executed.

Using the test array you just constructed with the names that were provided, the work array appears as:

FRED]WILMA]BARNEY]BETTY^DINO

Consequently, it has only two attributes.

To test this function, choose the L option to locate a string. At the prompt to ENTER A STRING, enter "DINO" and press Return. The next prompt is:

IS IT AN (A)TTRIBUTE, (V)ALUE OR (S)UB-VALUE?

Enter an "A" and press Return.

The program now performs the LOCATE statement listed on line 85. At the time of execution, the variable STRING contains DINO. The variable WORK.ARRAY contains

FRED]WILMA]BARNEY]BETTY^DINO

The variable POSITION is determined by the LOCATE statement. Since the string DINO matches exactly the contents of attribute two of the array, the LOCATE statement stores the number 2 in the variable POSITION. Had the string not been found, POSITION would have remained unchanged.

Notice that the LOCATE statement must have the ELSE condition. This means that it also allows the THEN clause. The statements following the ELSE initiator are executed when the string being located is not found. The statements following the THEN clause are executed when the string being located is found.

Since the string you requested was found, and the attribute position at which it was found was stored in the variable POSITION, then the THEN clause on line 86 is executed. This assigns the value TRUE to the variable FOUND.

After execution falls out of the CASE construct, the next block of code displays the string and locations:


113 IF FOUND THEN
114 PRINT @(3,18) : @(-3) ; * CLEAR TO END OF SCREEN
115 PRINT @(3,18) : STRING: "WAS FOUND."
116 PRINT @(3,19): "ATTRIBUTE": @(15,19): AMC.POSITION
117 PRINT @(3,20): "VALUE": @(15,20): VMC.POSITION
118 PRINT @(3,21): "SUB-VALUE": @(15,21): SVMC.POSITION
119 END ELSE
120 PRINT @(3,18) : @(-3) : STRING: "NOT FOUND"
121 END

This section does one of two things: If the string was found, then it is displayed by the statement on line 115. The attribute, value, and subvalue positions at which it was found are displayed by lines 116, 117, and 118. If it was not found, then line 120 displays the string along with the message that it was not found.

Locating Values within Attributes

To retrieve a value from within an attribute requires the following general form of the LOCATE statement:

LOCATE(string.expression ,array.variable,amc.expression;...
... setting.variable) {THEN statement(s)} ELSE statement(s)

This form is illustrated in Example 11 in the following section of code:


092 CASE TYPE = "V"
093 GOSUB 700 ; * GET AMC TO SEARCH FOR VALUE
094 LOCATE(STRING,WORK.ARRAY,AMC;VMC.POSITION) THEN
095 FOUND = TRUE
096 END ELSE
097 FOUND = FALSE
098 END
099 SVMC.POSITION = 0

If you respond with the letter V, which indicates that you are attempting to locate a value, then this CASE statement evaluates true and the LOCATE statement on line 94 is executed.

Using the test array you just constructed, you can see that attribute one has four values:

FRED]WILMA]BARNEY]BETTY

To test this function, choose the L option to locate a string. At the prompt to enter a string, enter "BARNEY" and press Return. The next prompt is:

IS IT AN (A)TTRIBUTE, (V)ALUE OR (S)UB-VALUE?

Enter "V" and press Return.

The program now performs the LOCATE statement listed on line 94. At the time of execution, the variable STRING contains BARNEY. The variable WORK.ARRAY contains:

FRED]WILMA]BARNEY]BETTY^DINO

The variable POSITION is determined by the LOCATE statement. Since the string BARNEY matches exactly the contents of value three of attribute one in the array, the LOCATE statement stores the number 3 in the variable POSITION. Had the string not been found, POSITION would have remained unchanged.

Since the string you requested was found, and the value position at which it was found was stored in the variable POSITION, then the THEN clause on line 95 is executed. This assigns the value TRUE to the variable FOUND.

After execution falls out of the CASE construct, the next block of code spans lines 113-121 and is the routine to display the string and its location in the work array:


113 IF FOUND THEN
114 PRINT @(3,18) : @(-3) ; * CLEAR TO END OF SCREEN
115 PRINT @(3,18): STRING: "WAS FOUND."
116 PRINT @(3,19) : "ATTRIBUTE": @(15,19) : AMC.POSITION
117 PRINT @(3,20) : "VALUE": @(15,20) : VMC.POSITION
118 PRINT @(3,21) : "SUB-VALUE": @(15,21) : SVMC.POSITION
119 END ELSE
120 PRINT @(3,18) : @(- 3) : STRING: "NOT FOUND"
121 END

Since the string was found, it is displayed by the statement on line 115. The attribute, value, and subvalue positions at which it was found are displayed by lines 116, 117, and 118.

Locating Subvalues within Values

To retrieve a subvalue from within a value requires the following general form of the LOCATE statement:

LOCATE(string.expression,array.variable,amc.expression,...
... vmc.expression;setting.variable) {THEN statement(s)} ELSE statement(s)

This form is illustrated in Example 11 in the following section of code:


100 CASE TYPE = "S"
101 GOSUB 700 ; * GET AMC TO SEARCH FOR VALUE
102 GOSUB 800 ; * GET VMC TO SEARCH FOR SUB-VALUE
103 LOCATE(STRING,WORK.ARRAY,AMC,VMC;SVMC.POSITION) THEN
104 FOUND = TRUE
105 END ELSE
106 FOUND = FALSE
107 END

There were no subvalues in the array that you constructed. If you feel adventurous, reconstruct the array with one or more subvalues and test this function.

As an aside here, I would like to interject a controversial point: Avoid using subvalues. Now it's out in the open. The PICK/BASIC language is well suited to manipulating the three-dimensional item (record) structure of attributes, values, and subvalues. If you plan to write report programs in PICK/BASIC, then using subvalues is acceptable. The ACCESS retrieval language does not deal well with subvalues, however--and that's putting it politely. Subvalues may seem like a convenient method of structuring data items, but if you plan to use ACCESS to produce reports on subvalued data, be prepared for some very strange output.

USING LOCATE TO SORT STRINGS

The LOCATE statement, as introduced in this example, is typically used to search for a string within an item, and to report the position at which it was found. The ELSE clause is executed when the string being searched for is not found.

The LOCATE statement has the added ability to determine the position at which a string would belong within an attribute or value. This involves the use of sequencing parameters:


Code Meaning

'AL' Ascending, left-justified.
'DL' Descending, left-justified.
'AR' Ascending, right-justified.
'DR' Descending, right-justified.

These sequencing parameters appear in the LOCATE statement immediately after the setting.variable.

The sequencing parameter you choose is a function of two things: whether the data is to be in ascending (lowest to highest) or descending (highest to lowest) order, and whether the data contains any alphabetic characters. Left justification must be used on data that contains alphabetic characters, while right justification is used exclusively on purely numeric data. This is extremely important, because it has drastic effects on the sorting of data.

The various forms of the LOCATE statement, using the sequencing parameters, are illustrated in the following general formats.

Sorting Attributes within Items

This is the general form of sequencing parameters in the LOCATE statement for sorting attributes within items:

LOCATE(string.expression,array.variable;setting.variable;...
...'sequence.parameter') {THEN statement(s)} ELSE statement(s)

For example, suppose the array looked like this:

ARRAY = BARNEY^FRED^WILMA

This indicates that there are three attributes, currently in alphabetical order. Consider the following statement:

LOCATE("BETTY",ARRAY;POSITION;'AL') ELSE FOUND = FALSE

When this statement is executed, the string "BETTY" is not found. Since the sequence parameter "AL" is in effect, POSITION is assigned the value 2, since that is the position at which the string belongs, (BETTY is "higher" than BARNEY, and "less than" FRED.) As a side effect, the ELSE clause is also executed, since BETTY was "not" found.

Sorting Values within Attributes

This is the general form of sequencing parameters in the LOCATE statement for sorting values within attributes:

LOCATE(string.expression,array.variable,amc.expression;...
... setting.variable; 'sequence.parameter') THEN statement(s) ELSE statement(s)

For example, suppose the array looked like this:

ARRAY = 6666]7777]8888

This indicates that in attribute 13 of the array there are three values, which currently are in ascending numeric order. If the following statement were executed:

LOCATE("8010",ARRAY,13;POSITION; 'AR') ELSE FOUND = FALSE

The string 8010 would not be found. Since the sequence parameter 'AR' is in effect, POSITION is assigned the value 3, since that is the position at which the string belongs. (8010 is "greater than" 7777, and "less than" 8888.) As a side effect, the ELSE clause is also executed, since 8010 was "not" found.

Sorting values within attributes is much more common than the former example of sorting attributes within items. This is useful, for example, when a list of dates or money amounts is to be stored in ascending or descending order.

WHY YOU DON'T WANT TO SORT STRINGS WITH LOCATE

Although the power of the LOCATE statement to sort strings is tempting, it is used very infrequently because attributes which contain multivalues often have a relationship with other multivalued attributes in the same item. This is very common in applications.


File name = ORDER-FILE
Array name = ORDER.ITEM
Item-id = 4678
Attribute  Contents           Purpose
001        7777               Date of order.
002 13 Salesman code.
003 W101]J336]T807 multivalued list of inventory
parts that were ordered.
004 12]4]36 Multivalued list of quantities
ordered.
005 0]4]0 Multivalued list of quantities
backordered.
006 1345]677]1898 Multivalued list of prices of
each part ordered.

Fig. 13-9. Sample use of multivalues in items.


For instance, consider the proverbial "order" item shown in Fig. 13-9. This type of design is very common in Pick applications. The phenomenon illustrated by attributes three through six goes by several names. Some people call these parallel multivalues; others call them correlated sets. Some people call them bad design.

The point is this: The values in each attribute have a positional correspondence with the values in the other attributes. For instance, this hypothetical order item indicates that the customer ordered a product whose inventory part number is W101 (the first value of attribute three). It further appears that they ordered 12 of these products (the first value of attribute four) and that 0 (zero) were backordered (the first value of attribute five) and finally, the cost per unit is $13.45 (the first value of attribute six, which is stored in its internal format). Although this type of item structure is popular among programmers and analysts, it has some serious potential side effects.

POTENTIAL SIDE EFFECTS OF PARALLEL MULTIVALUES

Note: Remember that this was written a long time ago. The following section is way out of date, in that only a few remaining versions of Pick have item length restrictions. Most now allow unlimited item sizes. (jes 7/15/95)

All but a few implementations of Pick currently have an item size restriction. This means (at the moment) that no single item may exceed 32K (about 32,000) bytes. For the most part, this is not a problem_but it can be. Suppose, for example, that the structure of items in the order file were like the one just illustrated. This structure works fine for the majority of the items in the file, but what happens when you get an order that has 4000 or 5000 line items? Yes, this is exceptional, but that's the kind of problem you must anticipate. This structure has no provision for dealing with such a case. Eventually, the item gets so large that an "INSUFFICIENT WORKSPACE" message appears on the screen of the operator who is entering the order, and the program enters the PICK/BASIC debugger. The operator is not amused. At this point, there is no way to recover the item.


File name = ORDER-HEADER-FILE 
Array name = ORDER.HEADER
ITEM Item-id = 4678


Attribute Contents Purpose

001 7777 Date of order.
002 13 Salesman code.
003 3 Number of line items.

Fig. 13-10. Alternate item design without multivalues.



File name = ORDER-DETAIL-FILE
Array name = ORDER.DETAIL.ITEM

Item-ID Attribute Contents Purpose

4678*1 (the "first" line item)
001 W101 Single-valued item-id of
inventory part ordered.
002 12 Single-valued quantity ordered.
003 0 Single-valued quantity
backordered.
004 1345 Single-valued price of part
ordered.

4678*2 (the "second" line item)

001 J336 Single-valued item-id of
inventory part ordered.

002 4 Single-valued quantity ordered.
003 4 Single-valued quantity
backordered.
004 677 Single-valued price of part
ordered.

4678*3 (the "third" line item)

001 T807 Single-valued item-id of
inventory part ordered.

002 36 Single-valued quantity ordered
003 0 Single-valued quantity
backordered.
004 1898 Single-valued price of part
ordered.

Fig. 13-11. Second half of an alternate item design without multivalues.


Another popular method of designing items is through the use of multiple items, rather than multiple values within an item. Instead of keeping all of the order information in one file, the information could be spread out into two files, such as a "order header" and "order detail" file. The "order header" file contains just the "static," single-valued attributes. Using the former example, this appears as shown in Fig. 13-10.

The "order detail" file contains single-valued items that represent the line items of the order. Using the data from the previous example, there would be three separate items in this file, which would appear as shown in Fig. 13-11. Using this technique not only removes the potential item size problem, but it also makes it easier for ACCESS to deal with this file. Remember, ACCESS is only moderately friendly to multivalues, and hates subvalues.

Previous chapter Next chapter Top

 

Creative Commons License
Jonathan E. Sisk's "Pick/BASIC: A Programmer's Guide" by Jonathan E. Sisk is licensed under a Creative Commons Attribution 3.0 Unported License.
Based on a work at jes.com.