An array is a sequential field (indexed by integers) of ints, floats, Objects or dynamically typed elements. Arrays are the most frequently used type of data container in programming. Other container classes include Pools, Lists, Trees and HashTables.
Most arrays can be used like "lists", i.e. you can add(), insert() and delete() elements. Usually this is quite expensive in terms of CPU time since preceeding/succeeding elements have to be copied. Please use the List class if you need real lists.
From a technical point of view, an array is also an instance of a C++ API class, i.e. there are dedicated array classes for different element types:
int -> IntArray float -> FloatArray String -> StringArray Pointer -> PointerArray <c++class> -> ObjectArray <scriptclass> -> ClassArray var -> ValueArray
Each of these array classes supports the [] operator which is used to read or modify elements.
Example:
int ia[10]; // create IntArray with 10 elements
ia[2]=3; // set an array element
print ia[2]; // print an array element
The array size initialisation may be omitted since version 0.9.0.6. Example:
IntArray ia; // create empty IntArray object
ia[2]=3; // set an array element, resize/alloc array elements
print ia[2]; // print an array element
The alloc(), realloc() and free() methods are used to determine or change the size of an array resp. free its elements.
If an array is resized using the realloc() method, its old content is preserved if possible. If the new array size is smaller than the number of previously used elements, (oldNumElements-newMaxElements) will be discarded.
There are hardcoded limits for the number of elements in arrays which are by default set to
#define TKS_MAX_INTARRAYALLOCSIZE (1024*1024*128) #define TKS_MAX_FLOATARRAYALLOCSIZE (1024*1024*128) #define TKS_MAX_ARRAYALLOCSIZE (1024*1024*4) // objects
Since version 0.9.0.6 tkscript automatically resizes your array if writing to an index >= maxElements. This behaviour can be turned off with the commandline option "-nra, --noarrayautoresize".
Example:
IntArray ia; ia.alloc(10); // create IntArray with 10 elements
ia[5]=42; // set the 6. array element, also changes numElements
ia.realloc(20); // resize array
print ia[5]; // read an element
Each array keeps track of the number of elements actually in use (numElements) as well as the maximum number of elements available (maxElements). Upon creation of an array, the maxElements property is initialized with the value passed to the alloc() method, numElements will be set to 0, though.
The empty() method is used to reset numElements without actually discarding the elements.
Single elements can be added resp. removed by using the add(), insert() and delete() methods. This mode of operation resembles lists, hence the term array list. In contrary to real lists, the maximum list length is limited by the number of allocated elements, though.
Please use the List class if you need real lists.
Example:
float fa[100];
loop(fa.maxElements)
fa.add( rnd(1.0) );
fa.realloc(200);
loop(fa.MaxElements-fa.numElements)
fa.add( rnd(1.0) );
print "fa.numElements=" + fa.numElements;
fa.empty();
print "empty()";
print "fa.numElements =" + fa.numElements + " fa.maxElements =" + fa.maxElements;
Unlike regular arrays, Hashtables are not indexed sequentially. Instead, named keys (Strings) are used to calculate the actual (internal) index using a so called hash function.
Furthermore, each HashTable element may have its individual type which is determined by the expression result being assigned to it. If this result value represents an unbound pointer, no explicite pointer assignment is required to bind that pointer to a HashTable element slot.
Also see here.
Example:
HashTable ht;
ht["myint"]=42; // store an int value
ht["myfloat"]=1.23; // store a float value
ht["mystr"]="hello, world."; // bind constant pointer
ht["myobj"]=new IntArray; // bind r/w pointer to HashTable slot
if ht.exists("myobj") // test if hash entry exists
print "found slot \"myobj\"";
else
stderr "d'oh.";
ht.delete("myobj");
Example:
int n=150; // http://www.bagley.org/~doug/shootout/bench/hash2
HashTable hash1; hash1.alloc(10000);
HashTable hash2; hash2.alloc(10000);
int i=0;
for(i=0; i<10000; i++)
hash1["foo_" + i] = i;
String k;
loop(n)
foreach k in hash1
hash2[k] += hash1[k];
The values of array elements may be set by either assigning single values or by using the following array initializer expressions:
Example:
StringArray str <= [ "one", "two", "three" ];
FloatArray flt <= [ 1.1, 2.2, 3.3 ];
IntArray ia <= [ 1, 2, 3 ];
HashTable ht <=#["myint"=42, "myfloat"=1.23, "mystr"="hello, world.", "myobj"=new IntArray];
The first element of an array initializer determines the type of array to be created. HashTable initializers are preceeded by the # char.
Note: The return value (i.e. an array object) of an array initializer is constant, i.e. read-only!
Nevertheless, when the initialization expression is called more than once, the expression lists are evaluated again and the elements of the array are updated.
A multidimensional array is constructed by using an array of pointers that point to IntArrays, FloatArrays, StringArrays or PointerArrays:
Example:
int i;
PointerArray mda <= [ [1,2,3], [4,5,6], [7,8,9] ];
IntArray ia <= mda[0];
i=ia[2];
print "ia[2]=mda[0][2]="+i;
i=mda[2][1];
print "mda[2][1]="+i;
The HashTable class can store arbitrary datatypes (including pointers) therefore the declaration and handling of multidimensional hash tables is fairly straight forward:
Example:
HashTable mht <= #[
"ht1"=#["one"=1, "two"=2.2],
"ht2"=#["three"=3, "four"=4.4]
];
HashTable ht <= mht["ht2"];
print mht["ht1"]["two"];
Note:
Similar to array initializers, the #[]
hash table initializer returns constant objects. Nevertheless, when an initialization expression is called more than once, the expression lists are evaluated again and the elements of the hash table are updated.
A Pool is a container for uniform objects which are not accessed sequentially but rather using so called name-IDs.
The main advantage rests with the fast qAlloc() and qFree() methods; a linear search for free elements is not required even after many nested alloc/free calls.
Example:
Pool p; p.template=String; p.alloc(10);
int nameid;
loop(10) {
nameid=p.qAlloc();
p[nameid]="test"+rnd(1024);
}
foreach nameid in p
print "p["+nameid+"]="+p[nameid];
Example:
class MyPoolEntry {
String name;
}
function main() {
Pool pool;
int i;
int id;
MyPoolEntry ce;
String s;
pool.template=MyPoolEntry;
pool.alloc(32);
loop(10)
{
id=pool.qAlloc();
ce<=pool[id];
ce.name="poolentry_"+id;
}
i=0;
foreach id in pool
{
trace("foreach qFree i=="+i);
if(++i<5)
{
// detach entry, does not affect iterated list
pool.qFree(id);
}
else
break; // end foreach
}
foreach id in pool
{
ce<=pool[id];
s<=ce.name;
trace("foreach entry \""+s+"\"\n");
}
pool.free();
}
Lists are used to store an arbitrary number of dynamically typed, double-linked elements. A list is composed of ListNodes which can also be created on-the-fly using the list expression.
Examples:
ListNode l;
l = {1,2,new String}; // copy list (grab objects)
l <= {1,2}+{3,4,new String}; // concat lists (reference objects)
l <= {1,2}^{3,4,new String}; // concat lists (grab objects)
Also see list.tks, tgclso_lists.tks
A tree is basically a special form of a double-linked list and consists of TreeNodes which are linked to their predecessor (parent), to nodes on the same hierarchy level ("left") resp. to branching off sub-trees ("right"). Each TreeNode may furthermore store an object pointer to reference abitrary user data.
Example: (requires input.xml)
String s;
if(!s.loadLocal("input.xml", 1))
die "cannot load input file.";
TreeNode troot<=s.parseXML();
if(troot)
{
trace "tree has "+troot.numNodes+" nodes.";
HashTable ht;
troot.writeToHashTable(ht);
String key; foreach key in ht {
print "ht[\""+key+"\"]="+ht[key];
}
}
else
die "parseXML failed.";
The TkScript JIT compiler has special support for int and float arrays. The following things should be kept in mind when using the JIT:
Examples: see IntArray, juliaattractor.tks