|
Jonathan E. Sisk's Pick/BASIC: A Programmer's Guide WWW Edition January, 2000
Chapter 13 |
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 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.
Fig. 13-2. An example of the 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
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.
Fig. 13-4. Calculated GOSUB statements.
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.
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.
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.
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.
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.
Fig 13-5 Use of the INSERT function in Program Example 11.
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.
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)
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.
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.
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.
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.
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.
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 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:
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.
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:
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 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:
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:
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.
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:
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:
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.
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:
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.
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:
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.
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.
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.
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.
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.
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