Chapter 11: Advanced Data Types

Understanding Adanced Data Types

The previous chapter introduced the standard data types found in most computer programming languages and in Harbour specifically. However, Harbour is a very powerful programming language and it includes a number of advanced data types which enable you to write programs that do some very clever and powerful things.

This chapter introduces each of the advanced data types and provides an overview of what each of them is, what they can do and details some of the ways in which you can use then in your own programs.  In later sections of this guide, I will make use of many of these advanced data types where I will explain each specific example in much greater detail as they are    introduced into the ePortfolio project.

ARRAY

At a very simple level an array can be described as a single memory variable that is capable of storing more than one value and, quite often, an array can contain values that are of comprised of different data types. Each of the individual values within an array is known as an element of the array and they are identified by their own unique element identity. The power of arrays is that they are a powerful and simple way of managing a large amount of data held in memory by simply referencing or manipulating a single memory variable.

It may be easier to think of an array being a little like a shopping trolley containing a bag of baking potatoes, a packet of mushrooms, two steaks, a bag of asparagus and a bottle of wine. You could imagine the shopping trolley as being an array and each of the items in the trolley as being different elements each of which have their own unique identity. Next time you go shopping, think about this example and try to imagine how you would complete that shop without using a trolley; the same situation would apply when you have a lot of related data but didn't use an array.

Harbour includes a number of commands and functions are used to create and manipulate arrays; we'll explore some of these next.

Working With Arrays

To help you start to explore arrays with real code, I have created a small program which you can download and open with your chosen source code editor. Once you have saved the file to the MySourceCode folder naming it ArrayTest1.prg, compile the program using the "..\build ArrayTest1" command. Once you have explored using the program, I will explain the array related lines of code by referencing the relavent lines within the source code file.

You can download a copy of the source code file from here - ArrayTest1.PRG

Creating an Array

LOCAL aTrolley := ARRAY(0)

This is contained in line 9 of the source code file. Creating – or defining – an array is done in the same way as you would create any other type of variable with one key difference; you have to instruct Harbour to create an array! This is done by declaring the variable and then assigning it an array data type by calling the ARRAY(0) function. As you can see, I have told the ARRAY() function that I simply want an array created but I do not want any elements added which I did by passing the parameter 0 to the function.

I'm sure you have already worked out that this can be changed! If you look at the help documentation for the array function you'll see that it accepts a numeric parameter to inform the function how many elements to create; had I stated ARRAY(6) then an array with six elements with a default value of Nil would be created. We'll refer back to the help documentation on ARRAY() a little later when we cover multi-dimensional arrays!

Before we move on to the next line of code, I am sure you are wondering how to create an array with a certain number of elements and assign them each different values. To do that, you need to specify slightly different code such as LOCAL aTrolley := {"Potatoes", "Mushrooms", "Large Steak", "Small Steak", Asparagus", "Bottle of Wine"}.  Harbour automatically recognises any values contained within curly brackets – {} – as an array.

Adding Elements to an Array With AADD()

AADD(aTrolley, "Potatoes")
AADD(aTrolley, "Mushrooms")
AADD(aTrolley, "Large Steak")
AADD(aTrolley, "Small Steak")
AADD(aTrolley, "Asparagus")
AADD(aTrolley, "Bottle of Wine")

These are lines 18 to 23 of the source code file. The AADD() (which you can think of as "array add") is used, obviously, to add a new element with a specific value to an existing array. It accepts two parameters; firstly, the name of the existing array and, secondly, the actual value to assign to the new element. Harbour assigns a data type to the element automatically, so using AADD(aTrolley, 1) would add a new element with a data type of numeric and a value of 1. It is important to note that using AADD() increases the overall size of the array by one element every time it is called.

The opposite of array add – ADEL() – or "array delete" is discussed a little later on.

Reading an Array Element Value

ONCLICK this.Caption := aTrolley[1]

This is line 51 of the source code file. The line of code in this example, which appears in all of the button definitions (lines 59, 67, 75, 83 and 91), is a little more complicated because I have to ask you, for now, to ignore part of the code because it touches on the Object data type which I cover in further detail in the next chapter. For now, please accept that ONCLICK this.Caption tells the program what to do if the user clicks on the button which, quite simply, is to change the CAPTION value of the button if the button is clicked on.

The part that is important to know at this stage is how I "read" the value of an element stored in an array and that, in this example, is done by specifying aTrolley[1]. The square brackets after the array name are used to delimit the element number we want to access which, in this case, is element number 1. Being able to read an element within an array also means that you can manipulate it in other ways. For example, if you had an array that contained some numeric values, you could add the values of two elements quite easily –  aNumericArray[1] + aNumericArray[2] would add the values of the first and second array elements.

You might find that some people will refer to an array index or an element index when discussing arrays. All you need to know is that both of these terms refer to exactly the same thing as an arrays' element number.

Finding the Size of an Array With LEN()

ONCLICK this.Caption := STR( LEN(aTrolley) )

This is line 107 of the source code file. As above, please ignore the ONCLICK this.Caption for now and focus instead on instead on LEN(aTrolley). The LEN() function accepts an array name as an argument (or parameter if that is easier to understand) and it returns a numeric value corresponding to how many elements appear in the array; in effect, the size or "length" of the array.

You'd be forgiven for wondering why you would use a function on an array that doesn't start with an "A" and, indeed, there is a function called ASIZE(). We'll look at this function a little later on as this array manipulation function is used to increase or decrease the size of an array rather than work out how many elements are contained within an array which you must do by using LEN().

You will also note that I have had to ensure that my program will not crash when someone clicks on the button to see the size of the array. The LEN() function returns a numeric value and the BUTTON CAPTION expects a value with a character data type. I have, therefore, converted the numeric data type value returned by LEN() to its equivalent character data type value by using the conversion function STR().

Sorting an Array With ASORT()

ONCLICK SortArray(aTrolley)

This is line 120 of the source code file and the property I have assigned to BUTTON btn_09 simply tells the program to call a function called SortArray() whenever the user clicks on the button. Note that I have passed the name of the array I want the SortArray() UDF to sort by stating aTrolley between the two brackets. This must be done because aTrolley is declared as a LOCAL variable and can only been "seen" in the main function!

FUNCTION SortArray(aToSort)

   ASORT(aToSort)

   Win_1.btn_01.Caption := "Sorted 1"
   Win_1.btn_02.Caption := "Sorted 2"
   Win_1.btn_03.Caption := "Sorted 3"
   Win_1.btn_04.Caption := "Sorted 4"
   Win_1.btn_05.Caption := "Sorted 5"
   Win_1.btn_06.Caption := "Sorted 6"

RETURN Nil

This is the UDF (found from line 172 onwards) that will be called whenever the user clicks on BUTTON btn_09. It expects to receive a copy of the array to sort, so I include that at the start of the function definition by saying FUNCTION SortArray(aToSort).

The next line of code, ASORT(aToSort), calls the actual Harbour array sort function and it needs to know the name of the array to sort. In this example, the array only contains elements that are all character data types, so it will sort the array elements alphabetically. If you take a few moments to look at the HMG help for ASORT(), you will notice that it is much more powerful than I have discussed in this simple example. For example, it will accept three further arguments to sort only a certain number of elements by using nStart and nCount and even accepts a further argument called a code block (which I introduce later) to change the way the ASORT function works! As if that wasn't enough, ASORT also works on date, numeric and logical data types!

It would be, perhaps, a little ambitious to cover these more advanced subjects at the moment. However, it is likely that I will use these more advanced techniques with ASORT later on in this guide where I will provide you with more detailed examples and explanations.

The final lines in the ArraySort UDF – such as Win_1.btn_01.Caption := "Sorted 1" – refer to Object manipulation which is discussed in chapter 12. All you need to know for now is that these lines change the text you will see on each of these buttons.

Searching an Array With ASCAN()

ONCLICK MsgInfo("The Wine is Element Number " + STR(ASCAN(aTrolley, "Bottle of Wine")) )

This is line 134 of the source code file and the ONCLICK property is assigned to BUTTON btn_10 to do three things in total. You should already recognise that it calls the Harbour MsgInfo() function to display a pop-up message to the user and recognise that I concatenate the actual words of the message to a numerical value which is converted to a character data type by using STR().

The numerical value comes from the Harbour function, ASCAN() which means "array scan" (or search). The function accepts two arguments; firstly, the name of the array to evaluate and, secondly, the value to scan for. ASCAN works by looking at each element one by one and compares the elements value to the one that you are scanning for. When it finds an exact match, ASCAN returns the element number of the element that contains the first match. In other words, it will only return the element number of the first match and will not tell you the element numbers of any other elements that contain the same value. Where no match is found, ASCAN simply returns a value of 0.

Like most of the Harbour array functions, ASCAN works with character, date, logical, or numeric data types and it does accept other arguments to "customise" how the scan works. As before, it may be a little too early to cover this level of detail at this time.

Other Harbour Array Functions

The Harbour language contains a number of other functions which can be used to work with, or manipulate, arrays. These range in complexity and power and I recommend that you refer to the HMG Help for full details of each as the following is intended only as a brief introduction.

Find the Data Type of an Array Element With VALTYPE()

The VALTYPE(<xArgument>) function accepts a single argument which is then evaluated to find the data type of that argument. When it completes running, the function returns a single character value to represent the data type of the argument. This value will be "C" where the argument contains a value with a data type of Character, "D" for Date, "N" for Numeric, "L" for Logical, "U" for Nil, "A" for an Array, "M" for Memo, "B" for a Code Block and "O" for an Object.

An example of using this function would be;

MsgInfo( VALTYPE(aTrolley[1]) )

In this example, the VALTYPE() function evaluates element number 1 in an array called aTrolley. The MsgInfo() function will then display a pop-up message stating "C", "N" etc depending on the data type of element 1.

A similar function TYPE() can also be used but it has certain drawbacks and should be avoided in preference to the more accurate and reliable VALTYPE() function.

Increase or Decrease Array Size With ASIZE()

The ASIZE(<aName>, <nSize>) function accepts two arguments; firstly, the name of an existing array and, secondly, a numerical value to specify the new size of the array. Where the array is smaller than new size specified, additional elements are added to the end of the array to the specified size and each new element is given a value of Nil. Where the array is larger than the specified size, excess elements are removed from the end of the array until it is the specified size. For example;

LOCAL aTest := { 1, 2, 3 } // Create an array with three elements
ASIZE(aTest, 5) // Array now contains {1, 2, 3, Nil, Nil }
ASIZE(aTest, 2) // Array now contains {1, 2 }

Obviously, you should take great care when using this function, especially where you are reducing the size of an array that contains values in elements that are deleted.

Insert a New Element Into an Array With AINS()

The AINS(<aName>, <nPosition>) function inserts a new element into an array at the specified position but it does not change the actual size of the array. The newly inserted element is given a value of Nil and the element that was previously in that position (and any others to the right of it) is moved one element to the right. The last element in the array is then simply discarded. For example;

LOCAL aTest := { 1, 2, 3 } // Create an array with three elements
AINS(aTest, 2) // Array now contains {1, Nil, 2 }
AINS(aTest, 1) // Array now contains {Nil, 1, Nil }

As above, you should take great care when using this function especially when working with multi-dimensional arrays (which are covered later in this chapter).

Delete an Array Element With ADEL()

The ADEL(<aName>, <nPosition>) function deletes the element from the specified position completely and shifts all remaining elements up one position. It then adds a new element to the end of the array which is given a value of Nil. Thus, this function does not change the actual size of the array. For example;

LOCAL aTest := { 1, 2, 3 } // Create an array with three elements
ADEL(aTest, 2) // Array now contains {1, 3, Nil }
ADEL(aTest, 1) // Array now contains {3, Nil, Nil }

As before, you should take great care when using this function especially when working with multi-dimensional arrays (which are covered later in this chapter).

Fill an Array With a Value With AFILL()

The AFILL(<aName>, <xValue>) function accepts the name of an existing array and a value of any valid data type to assign to each of the array elements overwriting any existing values. For example;

LOCAL aTest := { 1, 2, 3 } // Create an array with three elements
AFILL(aTest, "Hi") // Array now contains {"Hi", "Hi", "Hi" }
AFILL(aTest, 99) // Array now contains {99, 99, 99 }

The function also accepts two further arguments which can be used to "customise" its operation. Passing it a numeric value for <nStart> tells the function to start at the specified element number whilst <span class="code">&lt;nCount&gt;</span> tells it how many elements to replace with the new value. For example;

LOCAL aTest := { 1, 2, 3, 4, 5 } // Create an array with five elements
AFILL(aTest, "A", 5, 1) // Array now contains {1, 2, 3, 4, "A"}
AFILL(aTest, 99, 2, 3) // Array now contains {1, 99, 99, 99, "A"}

As before, you should take great care when using this function especially when working with multi-dimensional arrays (which are covered later in this chapter).

Make a Copy of an Array With ACOPY()

The ACOPY(<aCopyArray>, <aNewArray>, [<nStart>], [<nCount>]) function accepts the name of the array to copy and the name of the array to copy elements to. It is very important to note that the new array must already exist and be exactly the same size as the existing array; if it is smaller, some elements from the source array will not be copied!

Furthermore, ACOPY does not always "duplicate" every single element in the source array. For instance, if elements in the array to copy contain other arrays, ACOPY will only create a reference, or "pointer" to these in the original array which could have unexpected consequences for your program.

LOCAL aOne := { 1, 2, 3, 4, 5 } // Create an array with five elements
LOCAL aTwo := { 9, 9, 9, 9, 9 } // Create an second array - five elements
LOCAL aThree := { 0, 0, 0 } // Create a third array - three elements
ACOPY(aOne, aTwo) // Array two now contains {1, 2, 3, 4, 5}
ACOPY(aOne, aThree) // Array three now contains {1, 2, 3 }
ACOPY(aOne, aThree, 3, 3) // Array three now contains {3, 4, 5 }

As you can see with the last example above, the function also accepts two further arguments which can be used to "customise" its operation. Passing it a numeric value for <nStart> tells the function to start at the specified element number (element 3 in this example) whilst <nCount> tells it how many elements to copy to the target array (3 elements in this example).

Make a Copy of an Array With ACLONE()

The ACLONE(<aCopyArray>, <aNewArray>, [<nStart>], [<nCount>]) function makes a complete clone of an existing array. It is preferable in many ways to ACOPY as you only need to ensure the new array already exists but do not have to ensure it is exactly the same size. The second advantage is that ACLONE makes a complete duplicate and it works on multi-dimensional arrays without any special handling.

LOCAL aOne := { 1, 2, 3, 4, 5 } // Create an array with five elements
LOCAL aTwo := { } // Create a second empty array
LOCAL aThree := { } // Create a third empty array
ACLONE(aOne, aTwo) // Array two now contains {1, 2, 3, 4, 5}
ACLONE(aOne, aThree, 2, 3) // Array three contains {2, 3, 4 }

As you can see with the last example above, the function also accepts two further arguments which can be used to "customise" its operation. Passing it a numeric value for <nStart> tells the function to clone elements from the specified element number (element 2 in this example) whilst <nCount> tells it how many elements to clone to the target array (3 elements in this example).

To help you explore some of the above functions in greater detail, please take some time to compile and run the sample program ArrayTest2.prg program which can be downloaded here.

Multi-dimensional Arrays

I said earlier that an array is capable of storing more than one value and, quite often, it can contain values made up of different data types. Given that an array is a data type in its own right, it follows that an array can also contain yet another array and it is completely possible to declare an array that is solely comprised of several other arrays!  These are known as multi-dimensional arrays and they can be quite difficult for the beginner to understand. To help you visualise these better, consider the example declaration of a multi-dimensional array below;

aComplexArray := { {"Name", "Age", "Sex", "Married" }, ;
              {"Jack", 25, "M", .T. }, ;
              {"Jill", 20", "F", .F. } ;
             }

The first thing you might notice is that this array looks rather like a table that comprises of three rows and four columns. If it helps, try viewing the multi-dimensional as if it was contained in a spreadsheet;

Multi-Dimensional Array Spreadsheet Demo

As you can see, the rows of the spreadsheet contain exactly the same information as I have used in the aComplexArray array. In this example, element 1 (aComplexArray[1]) contains {"Name", "Age", "Sex", "Married"} which is the same as row 1 in the spreadsheet view whilst element 2 (aComplexArray[2]) contains {"Jack", "25", "M", ".T." } (row 2 of the spreadsheet view).

At a technical level, the aComplexArray array contains 3 elements, each of which is another array. These are often referred to as either nested arrays, and sometimes as sub-arrays of an array and you can even think of them as arrays within an array; it doesn't matter what you call them as they all mean the same thing.

So, how do you access – or reference – the actual data contained in a multi-dimensional array? This is easy to understand in a spreadsheet where, for example, row 2, column 1, contains a value of "Jack".

To reference values stored in a multi-dimensional array in Harbour you have to direct the program to "navigate" to the specific element you wish to use. To understand this, let's explore a few lines of code;

// Create a 3 element array with 3 sub-arrays
LOCAL aComplexArray := { {"Name", "Age", "Sex", "Married" }, ;
                   {"Jack", 25, "M", .T. }, ;
                   {"Jill", 20, "F", .F. } ;
                  }

This line of code creates a LOCAL memory variable called aComplexArray which is assigned a data type of array (represented by {}). The array will have three elements, each of which is also an array that contains three elements and values each. In other words, it is a multi-dimensional array!

MsgInfo(aComplexArray[2, 1] ) // MsgInfo() will say "Jack"

This line of code tells the MsgInfo() function to evaluate element 2 of aComplexArray, then element one of the second sub-array. The value of the element at position [2, 1] is "Jack" which is the text that will appear in the resulting pop-up message.

MsgInfo(aComplexArray[1, 4] ) // MsgInfo() will say "Married"

In this example, MsgInfo() will evaluate element [1, 4] and "Married" will be displayed in the resulting pop-up message.

nTotalAges := aComplexArray[2, 2] + aComplexArray[3, 2]

In this line of code, a variable called nTotalAges is assigned the value that results from adding together element [2, 2] and element [3, 2] of aComplexArray. The result of this would be to assign a numeric value of 45 to nTotalAges.

Bigger Multi-dimensional Arrays

Without wanting to completely "blow your mind", it would be remiss of me to not point out that a sub-array within an array can also contain further arrays. You can think of these are being two, three, four (or so on) dimensional arrays. It is even completely valid to have nested arrays within some array elements but not in others!

For example, thinking back to my spreadsheet example, I might want to add another column which contains a list of each person's children but I only want to use a single cell to do this. This would be quite complicated to do with a spreadsheet as they are, for the average user, only two dimensional in nature (in other words, they contain single cells arranged in rows and columns). However, this is absolutely no problem for Harbour!

Representation of a Three Dimensional Array

The above diagram is my best attempt at representing a three dimensional array diagrammatically. To explore this concept further, let's explore some simple code examples;

LOCAL aBigArray1 := {}

In this line of code, I want to create a LOCAL memory variable called aBigArray1 which is simply an empty array containing no elements or values.

AADD(aBigArray1, {"John", "Smith", "Mr", "Married", "Customer"} )
AADD(aBigArray1, {"Dave", "Jones", "Mr", "Single", "Employee"} )

These lines of code use the AADD() function to add two new elements to aBigArray1 both of which are also array. Therefore, the aBigArray1 array now contains an array in element 1 and another array as element 2.

AADD(aBigArray1, { "Paul", "Brown", {"Mary", "Bob", "Billy"}, ;
     "Married", "Employee" } )

This line of code uses the AADD() function to add another new element to aBigArray1 which is yet another array. However, the sub-array also contains another array at element 3 which has three elements ({"Mary", "Bob", "Billy"}). Therefore, the aBigArray1 array now contains three elements; the first two are "simple" arrays whilst the third is an array that contains another array.

AADD(aBigArray1, { "Mike", "White", {DATE(), .F., .T.},
     {25, 5.25, 0.258456}, "Customer"} )

This line of code uses the AADD() function to a final new element to aBigArray1 which is yet another array. This sub-array also contains two further arrays at element position 3 and 4 both of which contain three elements of different data types.

When these calls to AADD() have completed, the aBigArray1 array will contain four elements in total. The first two are "simple" arrays whilst the third and forth elements both contain arrays within an array.

If you refer back to the multi-dimensional array diagram above, try to picture where each of the values we have added is actually positioned. For example, the value stored in aBigArray1[1, 5] is "Customer" whilst the value in aBigArray1[3, 3, 2] is "Bob".

To help you explore this in greater detail, please take some time to compile and run the sample program ArrayTest3.prg program which can be downloaded here. This example program explores the 3 dimensional array discussed in the examples above.

However, please note, the code file contains a number of advanced programming techniques that have not yet been explored in any detail up to this stage in the guide. You should focus on lines 30 to 44 where the multi-dimensional array is created and take it at face value that the rest of the program works. Feel free to try changing the values of some of the elements in the array and also try adding further elements to the array (I have included one to help get you started on line 44, simply remove // before AADD and then recompile the program).

Concluding Comments

I hope you are starting to see that arrays can be very large and complicated but you might not yet fully appreciate why they are so powerful in programming; they can be quite difficult to understand for a beginner! I hope that the above descriptions and examples have given you, at least, a basic grasp of what these are and where you might use them. I will be using arrays, including multi-dimensional arrays, and many of the array functions we briefly explored above a lot more in later chapters where I will, obviously, include detailed explanations of the code used in the hope that you will gain a much greater understanding of these.

Finally, before moving on, please note that arrays are another data type that is not valid in a database file. In other words, you cannot create, store or access an array and its contents in a database file.

CODE BLOCK

The code block data type was first introduced to the Clipper programming language in 1990 having never existed in previous versions of either Clipper or dBase. Harbour, thankfully, also supports this  extremely powerful programming tool.

At its most simple level, a code block is a data type that contains compiled Harbour code which can then be executed in your program. Now consider that you can create a code block comprised of executable code and assign it to a memory variable in exactly the same way as you would assign, for example, a character string to a variable. As if that wasn't impressive enough, you can also send the code block other data to work with such as other memory variables!

The result would be a variable that doesn't just contain a value, it accepts other data values to work with and it can actually "does something" in your program!

Creating a Code Block

bSimple := { | n1 | n1 + 10 }

In this simple example, I have created a memory variable called bSimple and assigned it a code block, which accepts a single argument called n1. Assuming that n1 has a numeric value, the code block would add 10 to it.

A code block is constructed by stating { | <arguments> | <instructions> }</span> where the curly braces ({}) delimit the start and end of the code block. Two pipe characters (||) after the opening curly brace surround the arguments (separated by commas if there is more than one) to be passed to the code block and the actual code to execute follows that (separated by commas if there is more than one instruction).

LOCAL bAddNum := { | n1, n2 | n1 * n2 , n1 + n2 }

In the above example, a variable called bAddNum is created and assigned a code block data type. The code block accepts two arguments, n1 and n2, and contains two instructions to execute, n1 + n2 and n1 * n1. Assuming that n1 and n2 both contain numerical values, they are first multiplied by one another and then n1 is added to n2. It is important to note that multiple instructions are executed one after the other from left to right and the end result produced by the code block will be the result generated by the final instruction.

Evaluating a Code Block with EVAL()

This is all very well, but how do you actually manipulate (or use) a code block? The simplest way is to use the code block evaluation function, EVAL().

MsgInfo(STR( EVAL(bAddNum, nNumb1, nNumb2) ))

This example contains three "nested" functions in a single line. Where functions are nested, the first one that is executed is always the one in the very centre which is AEVAL() in this example. When that completes running, the result is passed back to the next function (STR()) and the result of that function is then returned to the outermost function (MsgInfo()) . You already know that STR() converts a numerical value into a character string and that MsgInfo() displays a pop-up information message on the screen, so let's focus on EVAL().

A code block is evaluated by calling EVAL(<bCodeBlock>, <codeblockArguments>). The first argument is the name of the code block to evaluate followed by a list of the arguments to pass to the code block (separated by commas if there is more than one).

In the above example the code block to evaluate is called bAddNum and it requires two arguments. We previously declared the code block by stating LOCAL bAddNum := { | n1, n2 | n1 * n2 , n1 + n2 }, so nNumb1 and nNumb2 (assume these are numerical values) will be supplied to the code block to use as n1 and n2 within the code block. Remember that only the result of the final instruction contained within a code block is actually returned to EVAL(), which would be n1  + n2 in this example.

Evaluating Arrays With a Code Block Using AEVAL()

Code blocks are used in many of Harbour and HMGs built in functions and an excellent example of this is the array evaluation function, AEVAL(). The Harbour Help documentation for this function describes it as "an array function that evaluates a code block once for each element of an array, passing the element value and the element index as [code] block parameters". In other words, the function takes an array and uses the element number and value to "do something" with every element contained in the array.

LOCAL aSimple := {"John Smith", "Bob Harris", "Mary Black"}
AEVAL(aSimple, { | cVal, nIdx | MsgInfo("Full Name: " + aSimple[nIdx] ) } )

In this example, the AEVAL() function is supplied with two arguments; the first is the name of the array to evaluate, aSimple in this example, and the second is the code block to execute for every element contained in the array.

When it runs, AEVAL() will pass the elements value cVal and the element number nIdx to the code block in that order. The code block then displays a pop-up information message – MsgInfo() – to display each element one by one until it reaches the end of the array. Note that the instruction within the code block does "receive" the element value (cVal) but does not actually use it. A slightly different expression of this line of code is to state AEVAL(aSimple, { | cVal, nIdx | MsgInfo("Full Name: " + cVal )  } ). Were you to try this then cVal would be used instead of nIdx.

LOCAL aNames := { {"John ", "Smith"}, {"Bob ", "Harris"},
        {"Mary ", "Black"} }
AEVAL(aNames, { | nVal, nIdx | MsgInfo("Full Name: " + ;
          aNames[nIdx, 1] + aNames[nIdx, 2]) }, 2, 2 )

This more complicated example demonstrates how AEVAL can be used on a multi-dimensional array. Firstly, note that the AEVAL function here has two additional arguments towards the end – 2, 2 – which are used to "customise" its operation. These two numeric values tell the function to start at the specified element number and tells it how many elements to evaluate within the array (start at element two and evaluate two elements from that point onwards).

If the sample above was executed, the element "value" that would be passed to the code block as nVal would actually also be an array (the first one in this example would be {"Bob ", "Harris"}). The code within the code block would then display a pop-up information message to display the two elements contained within that sub-array concatenated into a message to display on the screen each time the code block is evaluated for the aNames array.

To help you explore code blocks in greater detail, please take some time to compile and run the sample program CodeBlocks1.prg program which can be be downloaded here.

Concluding Comments

Like arrays, code blocks are incredibly powerful and can be quite complicated to understand when you first encounter them. The above discussion on code blocks has been purposely kept fairly brief because there are so many different places and ways you can leverage the power of them that it would be impossible to explore every single example! I will be using code blocks a great deal more often later on in this guide and will provide detailed explanations of what each example does when it is used. That should, I hope, help you understand these in greater detail so that you can start to create and use your own code blocks.

Finally, also like an array, the code block data type cannot be used in a database file. In other words, you cannot create, store or access a code block and its contents in a database file.

MEMO

The MEMO data type is a special data type that in its most simple form is nothing more than a very large character string. These are especially useful for storing and manipulating quite large "chunks" of text (such as this paragraph) and using them instead of standard character strings has a number of advantages when you need to save such information into a database file to retrieve and use at a later date.

As these are most useful when used in a database, further details of how and when to use the MEMO data type will be included separately in a later section of this guide that covers Database Fundamentals.

The Object Data Type

The final advanced data type is the "Object". These are extremely powerful and the subject of objects and object orientated programming (or OOP) is a complex one deserving of its own dedicated chapter which follows next!

Chapter 10: Standard Data Types <<     (no next)