Home Web Design Programming Fairlight CMI Soap Box Downloads Links Biography About... Site Map

The Holmes Page Understanding Arrays

CA-Clipper Opportunities Tips and Tricks Networking Internal Errors Source Code CA-VO


An array is a collection of values. Or, put another way, it is a list. Or a bag full of things. 
An array can be created in a number of ways. The Initializing Arrays page shows some of them. Each item in the array is referenced by indicating its numeric position in the list, starting from item number 1. 
The following code declares a variable, assigns a three-element array to it, then displays one of the elements and the array's length.
    local anTest              // Declare a variable.
    anTest := {10, 20, 30}    // Assign to the variable.
    ? anTest[2]               // The second element is 20.
    ? len(anTest)             // Displays the length: 3.
CA-Clipper allows arrays to be manipulated very easily. Whereas in C or Pascal you would need to allocate memory for each element of the array (and you would probably use "pointers" anyway), CA-Clipper handles memory management for you and makes it simple to add an element to the array, using the aadd() function.
    aadd(anTest, 40)          // Add 4th element at the end.
    ? anTest[4]               // Displays 40.
    ? anTest[5]               // ERROR! No 5th element.
Note that aadd() adds an element to the end of the array. 
There are three other common array functions.
• adel() - Delete an array element. The adel() function does not change the size of an array. Higher elements are shifted down. The last position is filled with NIL (which acts as a placeholder).
   anTest := {10, 20, 30, 40}// Assign to the variable.
adel(anTest, 2) // Result: {10,30,40,NIL}
• ains() - Insert a NIL element into an array. The ains() function does not change the size of an array. Elements are shifted up to make room for the new value. The last element "falls off" the end.
   anTest := {10, 20, 30, 40}// Assign to the variable.
ains(anTest, 2) // Result: {10,NIL,30,40}
• afill() - Fill an array with a specified value.
   anTest := {10, 20, 30, 40}// Assign to the variable.
   afill(anTest, 0)          // Result: {0,0,0,0}
   afill(anTest, "X")        // Result: {"X","X","X","X"}
   afill(anTest, .F.)        // Result: {.F.,.F.,.F.,.F.}
   afill(anTest, NIL)        // Result: {NIL,NIL,NIL,NIL}
The adel() and ains() functions are interesting in that they both do not change the length of the array. The designers of CA-Clipper chose this behaviour to give you more flexibilty. They left it to you to write functions that do change the array size, as in the following samples:
    function ADelete ( paArray, pnElement )
       adel(paArray, pnElement)
       asize(paArray, len(paArray) - 1)
    return paArray
    function AInsert ( paArray, pnElement )
       asize(paArray, len(paArray) + 1)
       ains(paArray, pnElement)
    return paArray
How asize() and aadd() allocate memory 
The aadd() function adds a single element to the end of an array. To add several elements aadd() must be called multiple times. In contrast, asize() sets the new size of an array in one shot: elements are added as a group. 
That's the key. Since each new element requires 14 bytes of memory, asize() attempts to allocate an amount of memory equal to 14 times the number of new elements. This block of memory is likely to be contiguous, whereas multiple calls to aadd() will allocate many smaller memory blocks which will probably be scattered throughout memory. 
This difference in memory allocation tends to improve the performance of an array that is enlarged with asize(). However, due to the dynamic nature of CA-Clipper memory management there are no guarantees, one way or the other. 
Although it is good to be aware of these issues, don't worry too much. Your job is to create the clearest source code. Let CA-Clipper take care of the memory management. 
If you worry about every byte, you might as well be a C programmer!

Arrays as Structures

A very nice feature of CA-Clipper is that an array can hold anything: numbers, dates, logicals, strings, etc. All at the same time. In other words, the elements of the array need not all be of the same data type, in contrast to C and Pascal.
    aPerson1 := {"Bob", 32, .T.} 
This array contains a string, a number, and a logical value. In other languages (such as C or Pascal) this "package" of information might be called a "structure" (in C) or "record" (in Pascal). The word "record" should bring to mind an actual database record that would be stored in a DBF file. A DBF record is a package of information which is made up of several fields. Each field can have a different piece of data in it. Many records (each with the same layout of fields) are collected together in a DBF file. 
Suppose that the aPerson1 array contains information about a person's name, age, and marital status. The following #defines can be created to indicate each value's position in the array:
    #define PERSON_NAME    1
    #define PERSON_AGE     2
    #define PERSON_MARRIED 3
Here are a couple more arrays, to represent more people:
    aPerson2 := {"Jane", 37, .T.}
    aPerson3 := {"Mark", 42, .F.}
Now the names can be printed like this:
    ? aPerson1[PERSON_NAME]
    ? aPerson2[PERSON_NAME]
    ? aPerson3[PERSON_NAME]
Note that the PERSON_NAME constant is not strictly necessary (it simply replaces the number 1), but it adds a good bit of readability to the code. 
Now, instead of working with the individual local variables, why not gather them up into another array, in the same way that many records are gathered up into a DBF file.
    // Create an array of "Person" arrays.
    aPeople := { aPerson1, ;
                 aPerson2, ;
                 aPerson3 }
    // Which is equivalent to this:
    aPeople := { {"Bob" , 32, .T.}, ;
                 {"Jane", 37, .T.}, ;
                 {"Mark", 42, .F.} }
The aPeople array looks like a two-dimensional matrix with 3 rows and 3 columns. This way of thinking can be helpful when starting to work with arrays, but can fall apart for more complex structures that do not have a nice square layout. 
Once the separate variables have been combined into an array, the names can be printed like this:
    for nL := 1 to len(aPeople)
       ? aPeople[nL][PERSON_NAME]
    next nL
The nL variable selects which person (or row) is of interest. Then the PERSON_NAME constant selects the first column of that row. 
Or, using CA-Clipper's aeval() function (which is basically a for...next loop):
    aeval( aPeople, {|e| qout( e[PERSON_NAME] } ) 
That example uses the qout() function which is equivalent to the ? command. The function is contained in a code block. For more about code blocks, see The Evolution of Code Blocks. 
You could add 10 years to everyone's age like this:
    for nL := 1 to len(aPeople)
       aPeople[nL][PERSON_AGE] += 10
    next nL
In a real CA-Clipper program, arrays are most commonly used for simple lists -- a pick list of countries, for instance.
    acCountries := {"Australia","Canada","China","France"}
    // Allow the user to select a country.
    nChoice := achoice(nTop, nLeft, nBottom, nRight, ;
How much memory does an array need? 
Every array element requires memory to store, even if its value is NIL. If an element contains a sub-array, memory is required to store its length. 
So, the number of memory elements used by an array is determined by multiplying the number of elements in every dimension and adding the sum of all dimensions except for the last. For example, a 300 by 200 by 100 array has (300 x 200 x 100) + 300 + 200 or 6,000,500 elements. 
Because every array element in CA-Clipper requires 14 bytes (not counting the memory required for its value), this amounts to 6,000,500 x 14 or 84,007,000 bytes -- well in excess of the theoretical capacity of the virtual memory system. 
The CA-Clipper documentation states that there is no benefit gained by hanging on to arrays and reusing them. CA-Clipper's memory management is much more efficient when strings and arrays are thrown away and rebuilt often rather than kept around unnecessarily for long periods.

Uses for Arrays

Another common technique is to use a two-dimensional array (similar the aPeople array above) to represent the main menu bar across the top of the screen and the drop-down menus. 
Or, a multi-dimensional array could store the report totals (as they are being accumulated) for each year, region, manager, salesman, and product. That's 5 dimensions. 
Also, all input screens that use the get command manipulate an array of get objects called GetList. Objects themselves are kind of like "smart" arrays -- self-contained packages of related information and functions. So the GetList variable is a kind of two-dimensional array. 
Another example is the @...prompt command which builds up an array of menu items. The menu to... command manipulates the array. 
You could say "arrays are everywhere in CA-Clipper", but that is not really the case. Use the right tool for the job. For example, many systems store picklists in DBF files (such as part numbers, client codes, etc.), so there is no reason to put the information in an array -- just use the dbedit() function or the tbrowse class directly on the table. 
Also, many programmers store menus in DBF files as well. Once that is done, it becomes quite easy to add a field to the table to give each menu item an access level or "rights mask" to control what users are allowed to do. In addition, translating the menu into another language is fairly straightforward. 
Arrays can only hold 4096 elements, whereas DBF files can contain millions of records. However, each element of an array can be another array (making it a two-dimensional array), which could give 4096x4096 elements in total. That's 16,777,216 elements. If each element takes up a few bytes of memory you can see that you could run out of memory pretty fast (see the sidebar How much memory does an array need? for details). 
You can create two-dimensional, three-dimensional, four-dimensional -- all the way to n-dimensional arrays, where n is infinity, subject to the amount of available memory. In other words, arrays can have any number of dimensions, but each dimension is limited to 4096 elements.

Home Web Design Programming Fairlight CMI Soap Box Downloads Links Biography About... Site Map

Site Map Send comments about this site to Greg at gregh@ghservices.com
All pages copyright © 1996-1999 GH Services™   Created 1997/11/18   Last updated 1999/09/30
All trademarks contained herein are the property of their respective owners