Tables¶
Tab values (tables) are used to define simple vectors and more. They can be defined by giving the list of their elements:
The tab
keyword is case-insensitive and optional. For
example:
$t := tab [0, 1, 2, 3] ; or
$t := [0, 1, 2, 3] ; the `tab' keyword is optional
these statements assign a tab with 4 elements to the variable
$t
.
Tables are compared in lexicographical order. In a boolean expression, an empty tab is false. Otherwise, it's true.
Elements of a tab can be accessed through the usual square bracket
...[...]
notation. Element indexing starts at 0, so $t[n]
refers to the (n+1)th element of the tab refered by
$t
. Notice that the arguments of the square bracket are
expressions1. Elements of a tab can be any kind of value,
even a tab, which would create a multidimensional array. Additionally,
one tab can contain multiple different types of objects.
The ForAll action can be used to iterate through all of the elements in a tab. A tab comprehension can be used to build new tab by filtering and mapping tab elements. There are also several predefined functions to transform a tab.
Multidimensional tab¶
Elements of a tab are arbitrary, so they can be other tabs. Nested tabs can be used to represent matrices and multidimensional arrays. For instance:
[ [1, 2], [3, 4], [4, 5] ]
is a 3 \times 2 matrix that can be interpreted as 3 lines and 2
columns. For example, if $t
is a 3 \times 2 matrix,
then the first element of the second line is accessed by the expression
$t[1][0]
$t[1, 0] ; equivalent form
The function @dim can be used to query the dimension of a tab, that is, the maximal number of tab nesting found in the tab.
A multidimensionnal array is a homogeneous tab: a tab of tab
elements is an multidimensional array (of dimension 1
) and
a tab whose elements are multidimensionnal arrays of the same dimension
and size are also multidimensional arrays.
If a tab's argument is a multidimensional array, the function @shape returns a tab of integers wherein the element i represents the number of elements in the ith dimension. For example
@shape( [ [1, 2], [3, 4], [4, 5] ] ) → [3, 2]
The function returns 0
if the argument is not a
well-formed (dimensionally consistent) array. For example
@shape( [1, 2, [3, 4]] ) → 0
Note that for this argument, @shape returns ::antescofo 0
because
the argument is a tab nested into a tab, but it is not an array because
the element of the top-level tab are not homogeneous. The tab
[ [1, 2], [3, 4, 5] ]
also fails to be an array, despite that all elements are homogeneous, because these elements are not of the same size.
Tab Comprehension¶
If a tab is specified by giving the list of its elements, the definition
is said in extension. The tab [ ... ]
construction is
an expression that defines a tab in extension.
A tab can also be defined in comprehension. A tab comprehension is an expression to build a tab from an existing tab or on some iterators. The general form of a tab comprehension is
[ output_expression | $var in input_set , predicate ]
where output_expression
is an expression, $var
is a variable identifier, input_set
is an expression
evaluating to a tab or an integer or the construction e₁.. e₂ : e₃
, and predicate
is a boolean expression. More
precisely:
This construction follows the form of the mathematical set-builder notation (set comprehension). For instance
[ e | $x in t ]
generates a tab of the values of the output expression
e
by running through the elements specified by the input
set t
. If t
is a tab, then takes all the
values in the tab. For example:
[ 2*$x | $x in [1, 2, 3] ] → [2, 4, 6]
The input set t
may also evaluate to a numeric value
n: in this case, take all the numeric values between 0 and n
excluded by unitary steps:
[ $x | $x in (2+3) ] → [0, 1, 2, 3, 4]
[ $x | $x in (2 - 4) ] → [0, -1]
Note that the variable $x
is a local variable visible
only in the tab comprehension: its name is not meaningful and could be any
variable identifier (but beware that it can mask a regular variable in
the output expression, in the input set or in the predicate).
The input set can be specified by a range giving the starting value, the step and the maximum value:
[ e | $x in start .. stop : step ]
The specification start .. stop
specifies a range where
start
is included and stop
is excluded.
If the specification of the step is not given, it value is +1 or -1
following the sign of (stop - start)
. The specification
of start
is also optional: in this case, the variable
will start from 0. For example:
[ $s[$i] + $t[$i] | $i in @size($s) ]
creates a tab whose elements are the pointwise sums of $s
and $t
(assuming that they have the same
size). Notice that expression $i in (@size($s))
enumerates the indices of $s
. Expression
[ @sin($t) | $t in -3.14 .. 3.14 : 0.1 ]
generates a tab of 62 elements: \sin(-3.14), \sin(-3.04), ..., \sin(3.04).
Tab comprehension may specify a predicate to filter the members of the input set:
[$u | $u in 10, $x % 3 == 0] → [0, 3, 6, 9]
filters the multiple of 3 in the interval [0, 10). The expression used as a predicate is given after a comma, at the end of the comprehension.
Tab comprehensions are ordinary expressions, so they can be nested. This can be used to create a tab of tabs. Such a data structure can be used to make matrices:
[ [$x + $y | $x in 1 .. 3] | $y in [10, 20, 30] ]
→ [ [11, 12], [21, 22], [31, 32] ]
More Examples of Tab Comprehension¶
Here are some additional examples of tab comprehensions to illustrate the syntax:
$z := [ 0 | (100) ] ; builds a vector of 100 elements, all null
In this example, the iterator variable is absent. In this case, the input set is constrained to be an expression between parentheses and evaluating to either a number or a tab (it cannot be a range).
$s := [ $i | $i in 40, $i % 2 == 0 ] ; lists the even numbers from 0 to 40
$t := [ $i | $i in 40 : 2] ; same as previous
$u := [ 2*$i | $i in (20) ] ; same as previous
; equivalent to ($s + $t) assuming arguments of the same size
[ $s[$i] + $t[$i] | $i in @size($t) ]
$m := [ [1, 2, 3], [4, 5, 6] ] ; builds a matrix of 3x2 dimensions
$m := [ [ @random() | (10) ] | (10) ] ; builds a random 10x10 matrix
; transpose of a matrix $m
[ [$m[$j, $i] | $j in @size($m)] | $i in @size($m[0])]
; scalar product of two vectors $s and $t
@reduce(@+, $s * $t)
$v := [ @random() | (10) ] ; builds a vector of ten random numbers
; matrice*vector product
[ @reduce(@+, $m[$i] * $v) | $i in @size($m) ]
; squaring a matrix $m, i.e. $m * $m
[ [ @reduce(@+, $m[$i] * $m[$j]) | $i in @size($m[$j]) ]
| $j in @size($m) ]
Mutating a tab's element¶
A tab is a mutable data structure : one can change an element within this data structure. Although a similar syntax is used, changing one element in a tab is an atomic action different from the assignment of a variable. For example
let $t[0] := 33
$t[0] := 33 ; the 'let' is optional if the tab is denoted by a variable
changes the value of the first element of the tab referred by
$t
to the value 33
. The general syntax is:
Unless the tab is referred to by a variable,
the let
keyword is mandatory. It is required when the
expression in the left hand side of the assignment is more complex than
a variable, a simple reference to an array element or a simple access to
a local variable of an exec. See sect. Assignment.
Because the tab to mutate can be referred to by an arbitrary expression, one may write something like:
$t1 := [0, 0, 0]
$t2 := [1, 1, 1]
@fun_def @choose_a_tab() { (@rand(1.0) < 0.5 ? $t1 : $t2) }
let @choose_a_tab()[1] := 33
that will change the second element of a tab chosen randomly between
$t1
and $t2
. Notice that:
let @choose_a_tab() := [2, 2, 2] ; invalid statement
raises a syntax error: this is neither a variable assignment nor the update of a tab element (there are no indices to access such element).
Elements of nested tabs can be updated using the multi-index notation:
$t := [ [0, 0], [1, 1], [2, 2] ]
let $t[1,1] := 33
will change the tab referred by to [[0, 0], [1, 33], [2,2]]
. One can change an entire “column” using partial indices:
$t := [ [0, 0], [1, 1], [2, 2] ]
let $t[0] := [33, 33]
will produce [[33, 33], [1, 1], [2, 2]]
. Nested tabs are
not homogeneous, so the value in the r.h.s. can be anything.
Sharing and copying a tab¶
In the introduction of section Values, we mention that a compound value is refered through a handle (or pointer). So, the same compound value can be shared between variables or shared between nested data structures.
This can be illustrated by the following example:
let $t := [0, 0, 0]
$u := $t
let $t[0] := 333
print $u ; will print [333, 0, 0]
After the assignment to $t
, the value referenced by
$u
has mutated, because the same tab is referenced by
$t
and $u
.
Assignment of a tab, and more generally a compound value, does not imply the copy of the referenced value. The function @copy can be used to create a fresh value:
let $t := [0, 0, 0]
$u := @copy($t)
let $t[0] := 333
print $u ; will print [0, 0, 0]
The sharing of data structures is useful: the same tab can be referred to
from many different places in the score and one update is visible by all
the tab referrers. However, it may be troublesome. The following code is
intended to create a 3×3 null matrix $m0
let $row := [0, 0, 0]
let $m0 := [ $row, $row, $row ]
but this is not exactly the case. As a matter of fact, if we want to
turn $m0
in the identity matrix using assignment:
let $m0[0, 0] := 1
let $m0[1, 1] := 1
let $m0[2, 2] := 1
what we obtain is a a 3×3 matrix full of 1
: because all
the row of $m0
refers to the same tab.
Assignment versus Mutating a tab's element¶
Changing the value of a tab's element is not a variable assignment: the variable has not been “touched”, it is the value referred by the variable that has mutated.
The difference between variable assignment and mutating one element in a tab is more evident in the following example:
let [0, 1, 2][1] := 33
where it is apparent that no variable at all is involved. The previous
expression is perfectly legal: it changes the second element of the tab
[0, 1, 2]
. This change will have no effect on the rest of
the program because the mutated tab cannot be refered elsewhere but this
does not prevent the action to be performed.
An important consequence is that mutating a tab elements does not trigger a whenever even if a variable is involved in the assignment. It is however very easy to trigger a whenever watching a variable referring to a tab, after the update of an element: it is enough to assign it to itself:
$t := [1, 2, 3]
whenever ($t[0] == 0) { ... }
let $t[0] := 0 ; does not trigger the whenever
$t := $t ; the whenever is triggered
We can mutate the first element of $t
but this does not
trigger the whenever. To do so, we assign the variable to itself. As a
matter of fact, a whenever watches the assignment of a set of
variables , NOT the mutation of the values referred by these variables.
Listable Operators¶
Usual arithmetic and relational operators are listable (cf. other listable functions in annex Library).
When an operator op is marked as listable, the operator is extended to accept tab arguments in addition to scalar arguments. Usually, the result of the application of op on tabs is the point-wise application of op to the scalar elements of the tab. But for relational operators (predicates), the result is the predicate that returns true if the scalar version returns true on all the elements of the tabs. If the expression mixes scalar and tab, the scalars are extended pointwise to produce the result. So, for instance:
[1, 2, 3] + 10 → [11, 12, 13]
2 * [1, 2, 3] → [2, 4, 6]
[1, 2, 3] + [10, 100, 1000] → [11, 102, 1003]
[1, 2, 3] < [4, 5, 6] → true
0 < [1, 2, 3] → true
[1, 2, 3] < [0, 3, 4] → false
Tab manipulation¶
Several functions exist to manipulate tabs intentionally, i.e., without referring explicitly to the elements of the tab. We briefly describe some of these functions. The Library exhaustively describes all tab related functions (click on function name to access the page dedicated to the function).
- @car
(t)
- returns the first element of
t
if is not empty, else it returns an empty tab.
- @cdr
(t)
- returns a new tab corresponding to
t
deprived of its first element. Ift
is empty, returns an empty tab.
- @clear
(t)
- shrinks the argument to a zero-sized tab
(no more elements in
t
, which is modified in-place).
- @concat
(t₁, t₂)
- returns the concatenation
t₁
of andt₂
.
- @cons
(v, t)
- returns a new tab made of
v
in front oft
.
- @count
(t, v)
- returns the number of occurrences of
v
in the elements oft
. Also works on string and maps.
- @dim
(t)
- returns the dimension of
t
, i.e. the maximal number of nesting in the elements oft
. Ift
is not a tab, the dimension is 0. A ‟flat” tab (a vector) has dimension 1.
- @drop
(t, n)
- gives a copy of
t
with its firstn
, elements dropped ifn
is a positive integer, and with its last elements dropped ifn
is a negative integer. Ifn
is a tab of integers, returns the tab formed by the elements of whose indices are not inn
.
- @empty
(t)
- returns true if there is no element in
t
, and false elsewhere. Also works on maps.
- @find
(t, f)
- returns the index of the first element of
that satisfies the predicate
f
. The first argument off
is the index of the element and the second is the element itself. Works also on strings and maps.
- @flatten
(t)
- builds a new tab where the nesting
structure of
t
has been flattened. For example,@flatten ([[1, 2], [3], [[], [4, 5]]])
returns[1, 2, 3, 4, 5, 6]
.
- @flatten
(t, l)
- returns a new tab where
l
levels of nesting have been flattened. Ifl == 0
, the function is the identity. Ifl
is strictly negative, it is equivalent to @flatten without the level argument.
- @gnuplot
(t)
- plots the elements of the tab as a curve
using the external command
gnuplot
. See the description @gnuplot in the index for further information and variations.
- @insert
(t, i, v)
- inserts “in place” the value into the tab after the index . If is negative, the insertion takes place in front of the tab. If \leq the insertion takes place at the end of the tab. Notice that the function is overloaded and applies also on maps. The form is also used to include a file at parsing time.
- @is_prefix, @is_suffix and @is_subsequence
- operate on tabs as well as on strings. Cf. the description of these functions in library.
- @lace
(t, n)
- returns a new tab whose elements are
interlaced sequences of the elements of the
t
subcollections, up to sizen
. The argument is unchanged. For example:@lace([[1, 2, 3], 6, ["foo","bar"]], 12)
returns[ 1, 6, "foo", 2, 6, "bar", 3, 6,"foo", 1, 6, "bar" ]
.
- @map
(t, f)
- computes a new tab where the
i
th element has the valuef(t[i])
.
- @max_val
(t)
- returns the maximum element among those
of
t
.
- @member
(t, v)
- returns true if
v
is an element oft
. Also works on string and map.
- @min_val
(t)
- returns the minimum among the
elements of
t
.
- @normalize
(t, min, max)
- returns a new tab with the
elements normalized between
min
andmax
. Ifmin
andmax
are omitted, they are assumed to be 0 and 1 respectively.
- @occurs
(t, v)
- returns the first index whose value equals the second argument. Also on string and map (the returned value is the corresponding key in a map).
- @permute
(t, n)
- returns a new tab which contains the
n
th permutation of the elements oft
. They are s! permutations, where s is the size oft
(and ! is the factorial function). The first permutation is numbered 0 and corresponds to the permutation which rearranges the elements oft
in an vector t_0 such that elements are sorted increasingly. The tab t_0 is the smallest element2 among all tab that can be done by rearranging the element oft
. The first permutation rearranges the elements oft
in a tab t_1 such that t_0 < t_1 for the lexicographic order and such that any other permutation gives a tab t_k lexicographicaly greater than t_0 and t_1. Etc. The last permutation (numbered s! - 1) returns a tab where all elements oft
are in decreasing order.
- @push_back
(t, v)
- pushes
v
to the end oft
and returns the updated tab (t
is modified in place).
- @push_front
(t, v)
- pushes
v
to the beginning oft
and returns the updated tab (t
is modified in place and the operation requires the reorganization of all elements).
- @reduce
(t, f)
- computes
f(... f(f(t[0],t[1]), t[2]), ... t[s-1])
wheres
is the size oft
. Ift
is empty, an undefined value is returned. If it has only one element, this element is returned. Otherwise, he binary operationf
is used to combine all the elements in into a single value. For example,@reduce(@+, t)
returns the sum of the elements oft
.
- @remove
(t, i)
- removes the element at index
i
int
(t
is modified in place). This function is overloaded and also applies to maps.
- @remove_duplicate
(t)
- keeps only one occurrence of
each element in
t
. Elements not removed are kept in order andt
is modified in place.
- @replace
(t, find, rep)
- returns a new tab in which a number of elements have been replaced by another. See full description at @remove_duplicate in library.
- @reshape
(t, s)
- builds an array of shape
s
with the element of tabt
. These elements are taken circularly one after the other. For instance@reshape([1, 2, 3, 4, 5, 6], [3, 2])
returns[ [1, 2],[3, 4], [5, 6] ]
.
- @resize
(t, s)
- increases or decreases the size of
t
tos
elements. Ifs
is greater than the size oft
, then additional elements will be<undef>
. This function returns a new tab.
- @reverse
(t)
- returns a new tab with the elements of in reverse order.
- @rotate
(t, n)
- builds a new array which contains the
elements of
t
circularly shifted byn
. Ifn
is positive the elements are right-shifted, otherwise they are left-shifted.
- @scan
(f, t)
- returns the tab
[ t[0],f(t[0], t[1]), f(f(t[0], t[1]), t[2]), ... ]
of the partial result of the reduction (see @reduce). For example, the tab of the factorials up to 10 can be computed by:@scan(@*, [$x : $x in 1 .. 10])
.
- @scramble
(t)
- returns a new tab where the elements
of
t
have been scrambled (their order is randomized). The arguments are unchanged.
- @size
(t)
- returns the number of elements of
t
.
- @slice
(t, n, m)
- gives the elements of
t
of indices betweenn
included up tom
excluded. Ifn > m
the element are given in reverse order.
- @sort
(t)
- sorts in-place the elements into ascending
order using
<
.
- @sort
(t, cmp)
- performs an in-place sort on the elements,
putting them in ascending order. The elements are compared using the function
cmp
. This function must accept two elements of the tab as arguments and returns a value converted to bool. The value returned indicates whether the element passed as first argument is considered to go before the second.
- @sputter
(t, p, n)
- returns a new tab of length
n
. This tab is filled as follows. The process starts with the first element int
as the current element. Successively for each element e in the result, a random number p' between 0 and 1 is compared withp
: if it is lower, then the current element becomes the value of e. If p' is greater, the element after the current element becomes the new current element and this element becomes the value of e.
- @stutter
(t, n)
- returns a new tab whose elements are
each elements of
t
repeatedn
times. The argument is unchanged.
- @take
(t, n)
- gives the first elements of
t
ifn
is a positive integer and the last elements oft
ifn
is a negative integer. Ifn
is a tab of indices, it gives the tab of elements whose indices are inn
.
Lists and Tabs¶
Antescofo’s tabs may be used to emulate lists
-
@car, @cons, @cdr, @drop, @last, @map, @slice and @take are similar to well known functions that exist in Lisp.
-
@concat returns the concatenation (append) of two lists.
-
Arithmetic operations on vectors are done pointwise, as in some Lisp variants.
In particular, the operators @cons, @car and @cdr can be used to destructure and to build a tab. They can be used to define recursive functions on tabs in a manner similar to that of recursive functions on lists. However, it builds a new tab unlike the operation cdr on list in Lisp. A tab comprehension is often more convenient and usually more efficient than a recursive definition.
The Library documents all Tab Manipulations @binary_search @car @cdr @clear [@clone] @concat @cons @copy @count @dim @domain @drop @empty @find @flatten @gnuplot @insert @iota @is_list @is_prefix @is_subsequence @is_suffix @lace @last @listify @map @max_val @median @member @normalize @occurs @parse @permute @push_back @push_front @range @reduce @remove @remove_duplicate @replace @reshape @resize @reverse @rotate @scan @scramble @size @slice @sort @sputter @stutter @succession @tab_history @tab_history_date @tab_history_rdate @take @to_num
-
The arguments of the square brackets are expressions so one can write, e.g.
(@f($x))[$n]
to access the value of the$n
th element of the tab returned by a function@f
. Parentheses are used to apply the brackets to the value returned by@f
instead on$x
. ↩ -
smallest for the
<
ordering. On tabs, this ordering corresponds to the lexicographic ordering. ↩