Containers

Containers are vars containing collections of values.  Containers are used for

C++Script containers are C++ containers that are vars, and contain vars.

Arrays

An array is a list of values, where each value can be referenced by a position.  Use arrays

Creating arrays

Arrays are created using the array() function to create an empty array:

var a = array();

Passing arguments to the array() function creates an array of with the given elements.  E.g.

a = array(1,2,3);

The bracket operator appends items to an array:

var b = array(1)(2)(3);
var c = array("this")("is")("an")("array");

What's happening here is that when you call an array var as a functor, then that value is appended to the array.      

An array can be initialized to a given size with a single value, using fill_array() e.g.

var a = fill_array(20, ""); // An array of 20 empty strings

The array_from() function converts another container (such as a string or a list) into an array.

Using arrays

Items can be created, updated and retrieved from arrays using square-bracket notation.

var a = array();
a[0] = "dog";
a["1"] = "cat";
a[2] = "mouse";
 
writeln(a[1]); // Output: cat
writeln(a.size()); // Output: 3

The size of the array does not need to be specified up front, since the array will automatically resize as items are added.  If you set an item beyond the end of the array, then the intermediate values are set to null.

var a = array();
a[100] = 12;
writeln(a[10]); // null
writeln(a.size()); // 101

If you retrieve an item that has not been assigned, or an item is retrieved beyond the end of the array, then the null value is returned without error.

The first item in the array has an index of 0.  If the index is negative, then the index is counted from the back (i.e. array[-1] refers to the last item in the array).

The number of items in the array is retrieved using the size() method.  The empty() method returns whether the array is empty.  An array converts to true if it is non-empty.

if(a) writeln("a is non-empty");

When written as a string, the array displays the contents in square brackets.

writeln(a);

The front() and back() methods return the first and the last members of the array respectively.  They are equivalent to a[0] and a[-1] respectively.  If the array is empty, then front() and back() return null is returned without error.

Modifying arrays

Items can be added and removed from the end of the array, using the push_back() and pop_back() methods respectively.  pop_back() returns the item that was removed, or null if the array is empty.

var a = array();
a.push_back(10);
a.push_back(9);
a.push_back(8);
writeln(a.pop_back()); // 8
writeln(a.pop_back()); // 9
writeln(a.pop_back()); // 10
writeln(a.pop_back()); // null

The clear() method erases all items in the array.  The erase() method erases a particular item, or a range of items.

var a = array('a','b','c','d','e','f');
a.erase( range(1,3) );
writeln(a); // [a,e,f]

Like all vars, the unary + operator clones the array.  This performs a "shallow copy".  The binary + operator appends two arrays, and the * operator repeats an array.  The += operator appends one array to another, and the *= operators repeats the array a specified number of times.  e.g.

var a = array(2)(3)(4);
writeln( a+a ); // [2,3,4,2,3,4]
writeln( a*3 ); // [2,3,4,2,3,4,2,3,4]

The var::insert() method can be used to insert an array of items into another array.  The first argument to insert() is the position that the item is inserted, and the second argument is an item to be inserted.

Lists

A list (or linked list) stores a sequence of values.  It is efficient to insert and delete items in the list, but it is not possible to look up an item at a particular index.  This is the opposite of an array (where it is very fast to look up a particular item, and slow to insert or delete from anywhere other than the end).  Lists are useful where

Creating lists

Lists can be created in the following ways:

 
var l1 = list(); // Empty list
var l3 = list(1)(2)(3)("horse");
var l4 = list(1,2,3,"horse");
var l5 = fill_list(10, "a") // The string "a" repeated 10 times

Using lists

The size() method returns the number of items in the list, and the empty() method returns whether the list is empty (size() != 0).  Converting to a bool (or var::as_bool()) returns true if the list is non-empty.  Converted to a string, the list is written as a comma-separated list in square brackets.

assert( !l1 );
writeln( items ); // [1,2,3,pony]
assert( l2.size() == 4 );

The front() and back() methods return the first and last member in the list respectively.  If the list is empty, then the methods return null but there is no error.

Lists do not have a [] operator to access individual members of the list.

Modifying Lists

The push_front() and push_back() methods add an item to either end of the list.  The pop_front() and pop_back() methods remove an item from either end of the list, and return the removed value.  If the list is empty, the pop_front() and pop_back() return null.

var l = list();
l.push_back(1);
l.push_back(2);
l.push_back(3);
writeln(l.front()); // 1
writeln(l.pop_back()); // 3
writeln(l); // [1,2]

The clear() method removes all items from the list.

List operators

The + operator joins two lists (the second operand can be any container), and the * operator repeats the list the specified number of times.  The += operator appends a container to the list, and the *= operator repeats the list.  e.g.

var l = list(1,2,3,4);
writeln(l + array(5,6,7)); // [1,2,3,4,5,6,7]
writeln(l * 2); // [1,2,3,4,1,2,3,4]
l+=list('a'); // [1,2,3,4,a]
l*=2; // [1,2,3,4,a,1,2,3,4,a]

Sets

A set is a list where each member is unique.  Sets store their members in order.  There is no numerical index, push_front(), push_back(), pop_front() or pop_back().

Creating sets

Sets are created using the functions set() and set_from().  The set() function creates an empty set, and arguments passed to the set() function create a set with the given members.  The () operator inserts items into the set.  set_from() converts another container into a set.  e.g.

var s1 = set(); // Empty set
var s2 = set(3)(2)(1)(3); // Set of 1,2,3 
var s3 = set(3,2,1,3); // Set of 1,2,3 

Using sets

Like all containers, var::size() returns the number of items in the set.  var::empty() returns true if the set is empty, and var::clear() removes all items from the set.  Values are inserted into a set using var::insert(), and removed using var::erase().  The var::contains() method returns true if the set contains a particular item.  e.g. (set.cpp)

#include <cppscript>
 
var script_main(var)
{
    var s1 = set();
    s1.insert(1);
    s1.insert(2);
    s1.insert(3);
    writeln( s1.contains(2) ); // true
    s1.erase(2);
    writeln( s1.contains(2) ); // false
    return 0;
}

The set operator + performs the union of two sets, * the intersection, and - the set difference.

Maps

A map (called a dictionary in Python) associates two different values, called keys and values, where a value is stored with each key.  Use maps when

Maps can compare any type of value provided that they are comparable.  Note that integers always compare lower to doubles, which compare lower to strings etc.

Creating maps

Maps are created using the map() function. 

var m = map();
var m = map(1,"dog")(2,"cat");

Using maps

The size() method returns the number of entries in the map, and the empty() method returns true if the map is empty.  A map converted to a bool is true when the map is non-empty.  The string form of a map lists the items in the map.

var m = map();
assert( !m );

The square brackets operator is used to get and set items in the map.  If you assign to an item in the map that doesn't exist, the item is created.

var pet_age = map();
pet_age["sammy"] = 4;
pet_age["necrox"] = 6.66;
assert( pet_age["sammy"] == 4 );

The keys in a map can be iterated using foreach.

foreach( pet, pet_age )
    writeln( pet + " is " + pet_age[pet] );

The values can be iterated using the values() function to iterate values instead of keys:

foreach( age, pet_age.values() )
    writeln(age);

If you retrieve a non-existent item in a map, the result is null.  Items can be erased from a map using the erase() method, and the presence of a key can be tested using the contains() method.

var m = map();
assert( !m.contains(1) );
m[1] = 12;
assert( m.contains(1) );
m.erase(1);
assert( !m.contains(1) );

Comparing containers

With the exception of strings (which are compared in lexographical order), containers are compared based only on whether they are the same object.  The ordering of containers is consistent (fully ordered), so they can be used as keys in maps.  The reason for this is because the comparison operator needs to be efficient and not go into an infinite loop.

Enumerating containers

The foreach macro repeats a block of code for each item in a container.  Use foreach

The syntax is

foreach( v, container )
    body of loop

Where v is the loop variable, and container is the container to be iterated.  The body of the loop is normally enclosed in curly brackets, which can be omitted if there is just a single statement in the loop.  e.g.

foreach( i, array(1,2,3) )
    writeln(i);

Arrays, sets, lists and strings iterate through the values in the container. 

If you wish to iterate the keys (indexes) in the array, the keys() method retrieves the keys instead of the values.  The following are equivalent:

foreach( i, a ) writeln(i);
foreach( i, a.keys() ) writeln(a[i]);

Maps and objects iterate through the keys of the container.  e.g.

var m = map();
m[1] = 2;
m[2] = 3;
 
foreach( i, m )
    writeln( "The value of " + i + " is " + m[i] );

The values of a map can be iterated directly using the values() method.

var m = map();
m[1] = 2;
m[2] = 3;
 
foreach( i, m.values() )
    writeln( i ); // 2, 3

The C++ container methods (begin(), end(), rbegin(), rend() etc) are also available.

Iterators are intended to be short-lived.  Iterators cannot be stored as members of other containers.  If an iterator detects that its underlying object has changed, then the iterator throws the exception "expired_iterator".

Ranges

A range is a special kind of container containing a linear sequence of numbers.  The six functions to create ranges are:

See range1.cpp for an example.

Converting between different containers

The  functions array_from(), list_from(), string_from() and set_from(), can be used to convert between containers of different types.  e.g.

var items_array = array("1","2","3");
var items_list = list_from(items_array);
var items_string = string_from(items_list);

Transform

The transform() function transforms the values of the container.  The transform() function doesn't create a copy or alter the original container.  Use transform where

The transform function takes two arguments: the container to be transformed, and a functor which transforms each value.  e.g.

var times_2(var a) { return a*2; }
 
foreach( i, transform( range(1, 10), times_2 ) )
    writeln(i);

Filter

The filter() function creates a container from another container with certain values removed.  The filter() function doesn't copy the values or alter the original container.  Use filter() where you need to iterate over some (but not all) items in a container.

The filter() function takes two arguments: the container to be transformed, and a functor which returns true or false.  e.g.

foreach( p, filter( range(2, 100), is_prime ) )
    writeln( p.as_string() + " is prime" );

The filtered result can be copied into another container as follows

var primes = array_from( filter( range(2, 100), is_prime ) );

Reverse

A container can be iterated in reverse using the reverse() function.  reverse() doesn't copy data, it merely causes the data to be iterated in reverse order.

foreach( i, reverse( array(1,2,3) ) )
    writeln(i); 

Tail

The tail() function enumerates all items in a container except the first element.  The tail() function doesn't copy the data or alter the original container.  e.g.

 
foreach( i, tail(array(1,2,3)) ) 
    writeln(i); // 2 3

tail() is not an efficient way to enumerate a list (i.e. avoid x=tail(x) in a loop).