Map Value¶
A map is a “dictionary” that associates a value to a key. The value can be of any type and so can the key:
map{ (k₁, v₁), (k₂, v₂), ... }
The map
keyword is case-insensitive and is followed by a
comma separated list of (key, value) pairs enclosed in braces. There is
an alternative notation where the pairs are written in an infix form
using the symbol ->
:
map{ k₁ -> v₁, k₂ -> v₂, ... }
The previous construction is an expression and keys and values in the definition list are ordinary expressions. An empty map is specified by an empty (key, value) list:
MAP{}
The types of the keys and values are not necessarily homogeneous. So a map may include an entry which associates a string to a number and later a map to a string, etc.:
map{ 1 -> "one",
"dico" -> map{ ("pi", 3.14), ("e", 2.714), ("sqr2", 1.414) },
true -> [0, 1, 2, 3],
1.234 -> (12 + 34)
}
A map is an ordinary value and can be assigned to a variable to be used later. The usual notation for function application is used to access the value associated to a key:
$dico := map{ 1 -> "first", 2 -> "second", 3 -> "third" }
...
print ($dico(1))
print ($dico(3.14))
will print
first
<undef>
The undef
value is returned for the second call because
there is no corresponding key.
Extensional Functions¶
A MAP can be seen as a function defined by extension: an image (the value) is explicitly defined for each element in the domain (i.e., the set of keys). NIMs are also extensional functions.
Extensional functions are handled as values in Antescofo. This is also the case for intentional functions, see chapter Functions.
In an expression, extensional functions or intentional functions can be used interchangably where a function is expected. In other words, you can apply an extensional function to get a value, in the same way you apply a predefined or a user-defined intentional function:
@fun_def @factorial($x) { ($x <= 0 ? 1 : $x * @factorial($x - 1)) }
$f := MAP{ (1,2), (2,3), (3,5), (4,7), (5,11), (6,13), (7,17) }
$u := $f(5) + @factorial(5)
$v := @map(@factorial, [1, 2, 3])
$w := @map($f, [1, 2, 3])
The computation of $w
shows that a MAP is passed as an
argument of the higher-order @map functions. Dot not confuse the
case-insensitive MAP
keyword with the name of the
function @map
. This function applies its first argument to
all elements of the tab passed as the second argument.
Domain, Range and Predicates¶
One can test if a map m
is defined for a given key
k
using the predicate @is_defined(m,k)
. This is not the same as testing the value returned by m(k)
is undef
because the key can be present in the
dictionnary with the value undef
.
The predefined @is_integer_indexed applied on a map returns true if all of its keys are integers. The predicate @is_list returns true if the keys form the set \{1, \dots, n\} for some n. The predicate @is_vector returns true if the predicate is satisfied and if every element in the range satisfies @is_numeric.
The functions @min_key and @max_key compute the smallest and largest value keys respectively amongst the keys of its map argument.
The functions @min_val and @max_val do the same for the values of its map argument.
In a boolean expression, an empty map acts as the value false
. Other maps are converted into the value true
.
The function @domain applied on a map returns the tab of its keys. In the returned tab, the keys are in increasing order. The function @range applied on a map returns the tab of its values. The order of the values reflects the order of their associated keys. For example
@domain({MAP{("zero", 0.0), ("0", 0), ("one", 1)}) → ["0", "one", "zero"]
@range({MAP{("zero", 0), ("0", 0), ("one", 1)}) → [0, 1, 0.0]
The functions @count, @find, @member and @occurs work on maps as well as on tab and string.
@member(m, v)
returns true
if there is a
key k
such that m(k) == v
and returns
false
otherwise.
@count(m, v)
returns the number of keys k
such that m(k) == v
.
@occurs(m, v)
returns the first key k
(for
the <
ordering) such that m(k) == v
if
such a key exists, else the undef
value.
Finally, @find(m, f)
returns the first key k
(for the <
ordering) such that f(k,m(k))
returns true and the undef value if such an entry does not exist.
Constructing Maps¶
The operations described below act on a whole map to build new maps.
@select_map restricts the domain of a map: @selec_map(m,P)
returns a new map n
such that n(x) =m(x)
if P(x)
is true, and undefined elsewhere. The
predicate P
is an arbitrary function (e.g., it can be a
user-defined function or a dictionary).
The operator @add_pair can be used to insert a new (key, val) pair into an existing map:
@add_pair(dico, 33, "doctor")
enriches the dictionary dico
with a new entry (no new map
is created). Alternatively, the overloaded function @insert can be
used: @insert can be used on tabs and maps; @add_pair is just the
version specialized for maps.
@shift_map(m, p)
returns a new map $n
such that `:::antescofo n(x+p) = m(x)
@gshift_map(m, f)
generalizes the previous operator
using an arbitrary function f
instead of an addition and
returns a map n
such that n(f(x)) = m(x)
@mapval(m, f)
composes function f
with the map
m
: the results n
is a new map such that
n(x) = f(m(x))
.
@map_compose(m, n)
builds a new map with keys taken in
the images of m
and values in n
for all
keys in the intersection of the keys of m
and
n
. In other words, if
m = MAP{ (k₁, m₁), (k₂, m₂), (k₃, m₃), ... }
n = MAP{ (l₁, n₁), (l₂, n₂), (l₃, n₃), ... }
constructs the map:
MAP{ ..., (mᵢ, nᵢ), ... }
if there exists an i
such that
m(i) = mᵢ
and n(i) = nᵢ
.
@merge combines two maps into a new one. The operator is asymmetric, that
is, if m = @merge(a, b)
, then:
m(x) = if (@is_defined(a, x)) then a(x) else b(x)
@remove(m, k)
removes the entry of key k
in map m
(no new map is created). If k
is
not present in m
, the command has no effect. This
function is overloaded and also applies to tabs.
Extension of Arithmetic Operators¶
Arithmetic operators can be used on maps: the operator is applied “pointwise” on the intersection of the keys of the two arguments. For instance:
$d1 := MAP{ (1, 10), (2, 20), (3, 30) }
$d2 := MAP{ (2, 2), (3, 3), (4, 4) }
$d3 := $d1 + $d2
print $d3
will print
MAP{ (2, 22), (3, 33) }
If an arithmetic operator is applied on a map and a scalar, then the scalar is implicitly converted into the relevant map:
$d3 + 3
computes the map MAP{ (2, 25), (3, 36) }
.
Map Transformations¶
@clear erases all entries in the map.
@listify applied on a map builds a new map where the keys have been replaced by their rank in the ordered set of keys. For instance, given
m = MAP{ (3, 3), ("abc", "abc"), (4, 4)}
@listify(m)
returns
MAP{ (1, 3), (2, 4), (3, "abc") }
because we have 3 < 4 < "abc"
.
Score reflected in a Map¶
Several functions can be used to reflect the events of a score into a map1:
-
@make_score_map returns a map where the key is the event number (its rank in the score) and the associated value, its position in the score in beats (that is, its date in relative time).
-
@make_duration_map returns a map where the key is the event number (its rank in the score) and the associated value, its duration in beats (relative time).
-
@make_label_pos returns, like the following, returns a map whose keys are the labels of the events and whose values are the position (in beats) of the events.
-
@make_label_bpm returns a map associating the event labels to the BPM at this point in the score.
-
@make_label_duration returns a map associating to the event of a label, the duration of this event.
-
@make_label_pitches returns a map associating a vector of pitches to the label of an event. A corresponds to a tab of size 1, a with n pitches to a tab of size n, etc.
These functions take two optional arguments, start and stop, to restrict where in the score the map is built. The map contains the key corresponding to events that are in the interval [start, stop] (interval in relative time). Called with no arguments, the map is built for the entire score. With only one argument start, the map is built for the labels or the positions strictly greater than start.
Variable's History Reflected in a Map¶
The sequence of the values of a variable is kept in a history. This history can be converted into a map: see section history reflected in a map.
List of Map (aka dictionnary)¶
@add_pair, @clear, @count, @domain, @find, @gshift_map, @insert, @is_defined, @is_function, @is_map, @listify, @make_duration_map, @make_label_bpm, @make_label_duration, @make_label_pitches, @make_label_pos, @make_score_map, @map, @map_compose, @map_concat, @map_history, @map_history_date, @map_history_rdate, @map_normalize, @map_reverse, @mapval, @max_key, @max_val, @member, @merge, @min_key, @min_val, @occurs, @range, @remove, @select_map, @shift_map
-
Aside from these functions, recall that the label of an event in $-form can be used in expressions as the position of this event in the score in relative time. ↩