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:


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


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    [@clone]    @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   

  1. 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.