Table of Contents
An array is a one or multi dimensional list of elements (data, values, objects). All elements in an array are homogeneous, i.e. all belongs to the same class. This chapter describes how to create and manipulate arrays in Biferno using the array class.
Arrays are normally used to group elements of similar nature under a
unique identifier for easier retrieval of the corresponding values. In
Biferno, as in several other programming languages, an array element is
denoted by the array name followed by the element index in the array
enclosed between square brackets. Indices start from 1. The first element
of the array myArray is denoted by myArray[1]. Values of array
elements can be read, written and printed as one would do for regular
variables.
Strings can be used as array indices in Biferno to implement a kind of array called “associative array”. Using strings as indices can be useful when array elements are associated themselves to specific categories, such as names, cities, professions, dates, etc.
Biferno array are completely dynamic. It is not necessary to specify the size of an array (the number of its elements) when the array is declared. Whenever we add a new element to an array that was previously initialized, the array is expanded automatically to accommodate the new element. This example shows how to initialize and fill a vector of strings with three elements.
<?
myArray = array()
myArray[1] = "John Doe"
myArray[2] = "San Francisco"
myArray[3] = "Accountant"
?>
The first instruction creates an object of the array class. Initially, an empty array - with zero elements - is created. The following instructions assign values to the element of the array, implicitly expanding the array, which will finally contain three elements.
The number of elements of an array is stored in the dim property of the
array class. This is a read-only property and can not be used to modify the
array dimension . Adding or deleting array elements can be achieved by
using specific methods of the array class.
The class of the first elements that is assigned to an array (the string class in our example above) implicitly specifies the class of all elements of the array. In Biferno the elements of an array must all belong to the same class. An attempt to assign to an element a value of a different class will result in automatic conversion of the value to the class of all other elements. If this is not possible the attempt will generate an error. If we write:
<?
myArray[4] = 32
?>
the fourth element of the array will be of the string class and its value
will be the numeric string "32", the result of the implicit conversion from
integer to string. An attempt to assign a non-numeric string to an element
of an array of integers will result in the Err_IllegalTypeCast error
because it is not generally possible to convert non numeric strings into
numbers.
The mechanism that allows automatic conversion of a value from the class to which it naturally belongs into another class is called implicit TypeCast. This important language feature will be described in Chapter 7, Operators and Expressions.
The GetElemClass method of the array class can be used to query the class
of the elements of an array.
Once initialized, elements of an array can be modified by simply assigning them new values. The syntax is the same we used to initialize variables.
Two things can happen when a value is assigned to an array element by referencing it via a numeric index. If the element already exists, its value will be replaced by the value of the entity on the right of the assign operator. If the element does not exists, the array is expanded by a number of elements equal to the difference between its current dimension and the specified index, and the new value is assigned to the last element added.
<?
myArray = array()
myArray[1] = 10
myArray[5] = 50
?>
The first instruction of the script initializes the first element of the array with the integer value 10. The second instruction expands the array with 4 elements and assigns the value 50 to the fifth element. The resulting array contains 5 elements.
What is the value of elements 2, 3, and 4? It is not defined. Elements of an array that are not specifically assigned, but simply added by automatic expansion, have no defined value and any operation on them (other than assigning them a value) results in an error.
Revisiting the initialization rules, the first example of this paragraph can be rewritten as:
<?
myArray = array("John Doe", "San Francisco", "Accountant")
?>
Here we are implicitly creating the array and assigning values to its elements, starting from the first and moving on in ascending order.
This is an example of initialization of an associative array:
<?
myArray = array("Name":"John Doe","Residency":"San Francisco",
"Profession":"Accountant")
?>
The syntax requires to specify, for each element to be implicitly assigned,
the name of the index followed by the ":" character and by the
corresponding value.
Two elements can not have the same name. An attempt to insert an element
with the same name of an existing element results in the
Err_DuplicatedArrayElemName error.
Array elements can be referred to by using the relative numeric index, as
in myarray[1]. Alternatively, elements can be referred to by
using the string specified at initialization time, as in
myarray["Name"]. The Index method, with prototype:
int Index(string elementName)
can be used to query the numeric index associated to a given element name.
The method accepts a string and returns the position of the corresponding
index, if it exists, or otherwise zero. Therefore the Index method can be
used to decide if a string corresponds to an array index. In the following
example the indexName string is received by the script as an input
parameter and its validity (as an array index) is verified before the
corresponding element of the myarray is printed:
<?
if (myArray.Index(indexName))
print(myArray[indexName])
else
print("Index \"" + indexName + "\" does not exist")
?>
It is not legal to expand an associative array by assigning a value to an
element which is referenced by a non existing index. Consequently, the
following code to add a new element with index "E-mail" to the myarray
array is not correct:
<?
myArray["E-mail"] = "doe@mydomain.com" // ERROR!
?>
The code above produces an error if the element with index "E-mail" does not exist. The correct code is as follows:
<?
myArray[myArray.dim + 1] = "doe@mydomain.com"
// Expand and assign
myArray.name[myArray.dim] = "E-mail"
// Rename index
?>
The name property of the array class is an array of strings containing the
names of the associative indices of the array. The name property can be
written, but in practice it is more convenient to use the Add method as
discussed in the following section.
We have described how an array is automatically expanded when an element is
initialized that had a numeric index greater than the current size of the
array. Alternatively, the size of an array can be directly increased (or
decreased) by using the SetDim method of the array class, with prototype:
void SetDim(int newDim)
The newDim parameter is the new array size. In the following example we
will expand the myArray array by 3 elements and then we will resize it to
its original size:
<?
curDim = myArray.dim
myArray.SetDim(curDim + 3)
myArray.SetDim(curDim)
?>
Remember that the dim property is read only. If the array does not contain
initialized elements, the new elements are undefined because their class
membership can not be determined. To remove all elements of an array we can
write:
<?
myArray.SetDim(0)
?>
The Add method of the array class allows to append one or more elements to
an array and, at the same time, assign them a value and optionally a name
for the new index. This method has prototype:
void Add(nonames obj elementN...)
The elementN parameter followed by three dots represents a list of objects
of any class The “obj” class is a kind of "abstract class" of Biferno, as we
will see in Chapter 11, User classes. The nonames clause, which
allows to specify a name for the new indices of the array, will be
described in Chapter 10, Functions. The following code expands the
myArray array by one element and assignes to the new element the value of
the num variable.
<?
num = 12
myArray.Add(num)
?>
Notice that the class of the object we are appending to the array must be the same as (or can be implicitly converted into) the class of the array elements that have been already assigned, if any.
In the following example, we append two elements to an associative array, specifying index names:
<?
myArray = array("Name":"John", "Cognome":"Doe")
myArray.Add("Username":"jdoe", "Password":"biferno")
?>
As shown, the syntax is <name index>:<value>. If elements are
appended with index names that already exist in the array, an error is
generated.
The Insert method of the array class allows to insert one or more elements
into an array. The prototype of this method is:
void Insert(nonames int pos, obj elementN...)
The pos parameter represents the numeric index of the first element to
insert. The elementN parameter has the same meaning as in the Add method.
In the following example, we append two new elements to an array of
strings, starting from the second element:
<?
myArray = array("John", "Frank", "Simon")
myArray.Insert(2, "Alan", "Donald")
?>
After the call to the Insert method, the myArray element contains five
elements (in this order) with values: "John", "Alan", "Donald", "Frank",
"Simon".
To remove one or more elements of an array, we can use the Delete method of
the array class, with prototype:
void Delete(int start, int end)
The start and end parameters are the index of the first and last element,
respectively, of the element block to be removed. Passing only the start
element results in removing one element. The following example shows how to
use this method:
<?
myArray = array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
myArray.Delete(5) // Delete the fifth element
myArray.Delete(5, 7) // Delete elements 5, 6 and 7
?>
The two calls to the Delete method will remove a total of four elements.
Following deletion, the myArray array consists of six elements with integer
values 1, 2, 3, 4, 9, 10. To delete all elements of an array we can write
(alternatively to the use of the SetDim method):
<?
myArray.Delete(1, myArray.dim) // Delete all elements
?>
The most efficient method for deleting all elements of an array if the use
of the Reset method, as in:
<?
myArray.Reset()
?>
Notice that it is always possible to initialize again an array by calling the constructor of the array class. In the following example we initialize an array of strings and, subsequently, call again the constructor on the same variable name, this time passing real numbers as elements:
<?
myArray = array("a", "b", "c")
myArray = array(1.6, 2.3)
?>
What is special about this operation is that not only the original array is deleted and built again on the base of the new elements passed to the constructor, but the class of the elements themselves is changed (in the example above from string to double).
Arrays can be concatenated in Biferno using the addition operator +.
Biferno operators will be illustrated in Chapter 7, Operators and Expressions. As
far as arrays are concerned it is sufficient to note that, in the example
below, the array3 array, sum of array1 and array2, has five elements with
values "a", "b", "c", "d", "e".
<?
array1 = array("a", "b", "c")
array2 = array("d", "e")
array3 = array1 + array2
?>
The arrays to be added (concatenated) must have elements of the same class or of "compatible" classes (the class of the elements of the first array in the chain prevails). It is legal to sum an array of numeric elements to an array of strings (the implicit typecast maps numbers to strings) but it is not possible to do the contrary.
When associative arrays are concatenated, elements names must be different, or an error will occur.
With the SubArray method of the array class it is possible to create a new
array consisting of a subset of consecutive elements extracted from another
array. This method has prototype:
array SubArray(int start, int end)
The start and end parameters indicate, respectively, the index of the first
and last element to be extracted, as in:
<?
array1 = array("a", "b", "c", "d", "e", "f")
array2 = array1.SubArray(2, 3) // array2 is array(b, c)
?>
When the operation is performed on associative arrays, the subarray
resulting from the application of the SubArray method has the same index
names as the original array.
Array elements can belong to any class, even to the array class. This allows to build arrays of arrays, also called multidimensional arrays.
A common use of two-dimensional arrays is in the representation of matrices or a tables. This example shows how to create and print a table containing a list of major cities in some US states:
<?
states = array(
"Ohio" :array("Columbus", "Cleveland", "Cincinnati","Dayton"),
"California" :array("San Jose", "San Francisco", "Sacramento", "Oakland"),
"Texas" :array("Houston ", "Dallas", "San Antonio", "Austin"),
"Florida" :array("Miami", "Tampa"))
?>
<html>
<body>
<?
nState = states.dim
for (state = 1; state <= nState; state++)
{
stateName = states.name[state]
print("<p>Major cities in <b>" + stateName +
"</b>:</p>\n")
nCit = states[state].dim
print("<ul>\n")
for (cit = 1; cit <= nCit; cit++)
{
print("<li>" + states[state][cit] + "</li>\n")
}
print("</ul>\n")
}
?>
</body>
</html>
This script produces the following output:
Major cities in Ohio:
* Columbus
* Cleveland
* Cincinnati
* Dayton
Major cities in California:
* San Jose
* San Francisco
* Sacramento
* Oakland
Major cities in Texas:
* Houston
* Dallas
* San Antonio
* Austin
Major cities in Florida:
* Miami
* Tampa
Notice the use of visual formatting in the declaration of the states array,
which allows to write more easily readable code, and the use of the for
flow control instruction, similar to the C language instruction with the
same name. Flow control instructions will be discussed in the following.
The array class has a method for implicit conversion to a string. The resulting string provides a textual representation of the array structure.
<?
array1 = array("Red", "Green", "Blue")
a1 = (string)array1 // a1 is "array(Red,Green,Blue)"
array2 = array("Name":"John", "Surname":"Smith")
a2 = (string)array2 // a2 is "array(Name:John, Surname:Smith)"
?>
Sometimes it can be useful to order the elements of an array, e.g. to print
a number of strings in alphabetic order. The array class makes the Sort
method available with prototype:
void Sort(int mode=asc, string compareFunc, int alg)
The optional parameter mode specifies the sorting order. The value for
this parameter can be specified as the constant asc (ascending) or the
constant desc (descending). The default is to sort element in ascending
order (from the smallest to the largest). The optional parameter
compareFunc allows to specify a comparison function. An example is given
below. If the elements of an array belong to one of the primitive classes
(strings, numbers and Booleans), the sorting criteria are those of the
class the elements belong to.
<?
array1 = array(5, 1, 4, 2, 3, 6)
array1.Sort() // array1 is array(1, 2, 3, 4, 5, 6)
array2 = array("b", "f", "a", "d", "c", "e")
array2.Sort(desc) // array2 is array(f, e, d, c, b, a)
?>
The optional alg parameter can be used to specify the sorting algorithm
that should be used. The supported values are bubble and shell. The default
is bubble if the array has less than 20 elements, shell otherwise.
When an associative array is sorted, index names are still associated to
the same elements after the sorting is completed - index names "follow" the
element they identify. Notice that the Sort method acts directly on the
input object and, in general, will modify it ("side effect").
If a comparison function is specified by passing its name to the Sort
method in the compareFunc parameter, the comparison function must have the
following prototype:
boolean CompareFileSize(array theArray, int index1, int index2, int mode)
The function must return the true value if the elements of the array with
indices index1 and index2 must be swapped in the sorting, false otherwise.
The mode parameter, if used, takes automatically the value of the parameter
with the same name in the Sort method. The first parameter is the array to
be sorted. In the following example we show how to sort an array of files
in descending order on the base of their byte size.
<?
function boolean CompareFileSize(array *ar, int ind1, int ind2, int mode)
{
if (mode == array.desc)
return (ar[ind2].length > ar[ind1].length)
else
return (ar[ind2].length < ar[ind1].length)
}
fileArray = array()
fileArray[1] = file("image1.gif")
fileArray[2] = file("image2.gif")
fileArray[3] = file("image3.gif")
fileArray[4] = file("image4.gif")
fileArray.Sort(desc, "CompareFileSize")
?>
The theArray parameter is passed by reference ("*" character in front of
the parameter name), and therefore could be modified within the
CompareFileSize function (see Chapter 10, Functions for an
explanation of passing parameters by reference). Here the parameter is
passed by reference only for performance reasons and should not be modified
within the function. To prevent a possible modification the array is locked
during the sorting operation. An attempt to modify it by the compareFunc
function would generate the Err_ObjectIsLocked error.
To invert the order of the elements of an array the Reverse method of the
array class can be used. This method has no parameters and does not return
any values:
<?
myArray = array(1, 2, 3)
myArray.Reverse() // The array is now array(3, 2, 1)
?>
The Find method allows to decide if an array contains an element with a
given value. If an element with the given value is found, the method
returns the index of the element, otherwise the method returns zero. This
method has prototype:
int Find(obj element)
An example is:
<?
myArray = array("Red", "Green", "Blue")
index = myArray.Find("Green") // index is 2
index = myArray.Find("Yellow") // index is 0 (element not found)
?>
The Min and Max methods return the index of the smallest and largest
element in an array, respectively. The criterion used to compare elements
depends on the class of the elements. An example is:
<?
array1 = array("John", "William", "Albert")
index_min = array1.Min() // index_min is 3
index_max = array1.Max() // index_max is 2
array2 = array(0.6, 12.4, 4.1)
index_min = array2.Min() // index_min is 1
index_max = array2.Max() // index_max is 2
?>
These methods are usually applied only to arrays containing string or numerical elements.