Отображения (мэпы, 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) = vm.set(k): vзаменить наm.!(k) = vm!(k) = vзаменить наm.!(k) = vm!k = vзаменить наm.!(k) = vm!(k)заменить наm.!(k)m!kзаменить наm.!(k)