Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Отображения (мэпы, Map[K, V], map[K, V])

Общая информация

Отображения (мэпы, ассоциативные массивы) не являются конструкцией языка, а реализованы при помощи обобщенных трейта Map[K, V] и метода map[K, V], определенных в пакете libretto.

Отображения позволяют привязывать значения (V переменная типа) к ключам (K переменная типа).

Порядок ключей не определен.

Это изменяемое отображение: изменения осуществляются “по месту”, а не возвратом нового экземпляра.

Тип Map[K, V]

Трейт Map[K, V] используется в качестве типа для отображения, определен в пакете libretto.

Тип ключа K допускается только без указания кардинальности (строго одно значение). Объединение допускается в качестве типа ключа.

Тип значения V не ограничен (может быть с кардинальностью). Типом значения может быть и трейт, и объединение. Но если допускается пустота, то ключ со значением всё равно будет удаляться при установке пустоты в качестве значения.

Требования к ключам

Ключ должен выполнять правила сравнения и вычисления хэша. Это автоматически выполняется для структур, но не выполняется для трейтов. Поэтому в качестве ключей можно использовать либо предопределенные структуры Int, Real, String, Unit, либо структуры. Статической проверки компилятором этого правила нет.

Поведение для структуры в качестве ключей может изменяться определением equalityKey для этой структуры. См. соответствующее описание.

Создание: map[K, V]

Для создания экземпляра пустого отображения используется метод map[K, V], определенный в пакете libretto. Результатом является экземпляр трейта Map[K, V].

def main = {
  fix m0 = map[Int, String]() // вывод типа
  fix m1: Map[String, String*] = map[String, String*]()
  println: m0.keys
  println: m1.keys
}

Выдаёт

()
()

Привязка значения: .!(v) = k

Для установки или изменения привязки значения к ключу используется вызов .!(k) = v, где k - значение ключа (нового или уже существующего), v - значение, которое будет привязано к ключу. Типы k и v соответствуют типизации Map. Тип результата самого метода - пустота ().

def main = {
  fix m = map[Int, String]()
  m.!(1) = "one"
  m.!(1) = "One"
  m.!(2) = "Two"
  println: m.keys
}

Выдаёт

(1,2)

Вызов метода установки != должен быть через точку и со скобками для ключа: m.!(1) = "one" - это правильно, m!1 = "one" или m!(1) = "one" - это неправильно (устаревшие конструкции, будут убраны).

Метод set для установки тоже не должен использоваться (устаревший, будет убран).

Если в качестве значения передаётся пустота (), то ключ и привязанное значение будут убраны из отображения:

def main = {
  fix m = map[Int, String]()
  m.!(1) = "one"
  m.!(1) = "One"
  m.!(2) = "Two"
  println: m.keys
  m.!(1) = () // удалить ключ 1
  println: m.keys
}

Выдаёт:

(1,2)
2

Удаление при значении () (пустота) происходит всегда. Вне зависимости от того, допускает ли типизация Map пустоту или нет.

def main = {
  fix m = map[String, String*]()
  m.!("abc") = ("a", "b", "c")
  println: m.keys
  m.!("abc") = ()
  println: m.keys
}

Выдаёт:

abc
()

Получение значения: .!(v)

Для получения значения, привязанного к ключу, используется метод .!(v). Если ключа нет, то возвращается пустота. Соответственно, типом результата будет V (из определения Map), но с кардинальностью, допускающей пустоту (? или *).

def main = {
  fix m = map[String, Int]()
  m.!("abc") = 123
  println(m.!("abc"))
  println(m.!("abc") + 777) // Int сложение
  println(m.!("def"))
  println(m.!("def") + 777)
}

Выдаёт:

123
900
()
()

В этом примере для Map[String, Int] результат получения ключа по .!(v) будет типа Int?, где () будет появляться в случае отсутствия ключа.

Имя !

Отображение можно рассматривать как функцию, которая по значению ключа возвращает либо привязанное значение, либо пустоту. В Libretto для подобных действий существует имя метода ! - оно имеет смысл apply (применить).

При определении метода имя берётся в обратных кавычках `!`, а при вызове можно их не указывать: !.

Для установки (привязки к ключу) значения тоже используется метод != с именем, содержащим !. Это принято для единообразия вызовов: m.!(k) = v для установки и m.!(k) для получения значения.

Последовательность ключей: keys

Для получения последовательности всех имеющихся ключей в отображении используется метод keys.

def main = {
  fix m = map[String, Int]()
  println: m.keys
  m.!("abc") = 123
  println: m.keys
  m.!("def") = 777
  println: m.keys
  m.!("ghi") = 100
  println: m.keys
  m.!("ghi") = ()
  println: m.keys
  m.!("def") = ()
  println: m.keys
  m.!("abc") = ()
  println: m.keys
}

Выдаёт:

()
abc
(abc,def)
(abc,def,ghi)
(abc,def)
abc
()

Ключ, которому выставляется значение пустоты (), удаляется из отображения.

Тип результата keys соответствует типу K в определении Map[K, V], но с кардинальностью *.

def main = {
  fix m = map[Int, Unit]()
  m.!(123) = unit
  m.!(777) = unit
  println: m.keys
  var sum = 0
  m.keys as key.${ sum = sum + key } // сложение Int
  println: sum
}

Выдаёт:

(777,123)
900

Устаревшие методы: set, ! и != без точек и скобок

Для установки и получения нужно обязательно использовать методы с ! в имени, через точку и со скобками для ключа. Остальные методы считаются устаревшими и будут убраны в будущих версиях

  • m.set(k, v) заменить на m.!(k) = v
  • m.set(k): v заменить на m.!(k) = v
  • m!(k) = v заменить на m.!(k) = v
  • m!k = v заменить на m.!(k) = v
  • m!(k) заменить на m.!(k)
  • m!k заменить на m.!(k)