Карта

Карта — терм, реализующий интерфейс ассоциативного массива, то есть позволяет хранить данные в виде (КЛЮЧ, ЗНАЧЕНИЕ). То же: хеш-таблица, ассоциативный массив, map.

Карты, как новый тип данных в Эрланге, появились относительно недавно, в версии 17 Erlang/OTP — в 2014 г.

1> Age = #{"Alex" => 18, "Bob" => 21}.
#{"Alex" => 18,"Bob" => 21}
2> maps:put("Alex", 19, Age).
#{"Alex" => 19,"Bob" => 21}
3> maps:put("Ivan", 33, Age).
#{"Alex" => 18,"Bob" => 21,"Ivan" => 33}
4> Age.
#{"Alex" => 18,"Bob" => 21}

В первой строке мы создали карту и привязали её к переменной Age. Подразумевается, что мы в этой карте храним возраст неких людей. Во второй команде мы использовали put/3 из модуля maps, предназначенного для работы с картами, которые по-английски называют maps — карты. maps:put/3 “положила” новое значение. Но раз уже есть пара с ключом “Alex”, она просто перезаписалась, и теперь Алексу 19 лет. В третьей команде мы опять использовали maps:put/3. Но Ивана в нашей карте ещё не было, поэтому появилась третья пара.

Если вы внимательны, то заметили, что после 3-й команды мы получили значение выражения, в котором Алексу почему-то снова 18 лет. Далее мы просим выдать значение, какое сейчас у Age. И оказывается, что в нашей карте по-прежнему два значения, а не три, и Алексу правда всё ещё 18 лет.

Всё правильно, карту, как и любой другой тип данных мы, конечно, можем трансформировать во что-то другое. Но переменные в Эрланге иммутабельны. В 1-й строке левая и правая часть с помощью знака равенства сопоставляются, и поскольку к переменной Age ещё не привязано какое-то иное значение, значит, будет намертво привязано это: #{"Alex" => 18, "Bob" => 21}.

5> Age2 = maps:put(“Ivan”, 33, Age). #{“Alex” => 18,“Bob” => 21,“Ivan” => 33} 6> Age2. #{“Alex” => 18,“Bob” => 21,“Ivan” => 33}

Здесь мы вводим переменную Age2, и ей уже присваивается значение выражения, в котором три записи. За основу берётся переменная Age, к которой привязана исходная карта.

Элементы-пары в карте хранятся в определённом порядке. Если ключи — целые числа, сортировка будет по ним.

1> Users = #{2=>boris,3=>carl,1=>alex}.
#{1 => alex,2 => boris,3 => carl}

Если мы зададим две карты с одинаковым набором элементов, но расположенными в разной последовательности, карты будут равны друг другу. Что интересно, порядок в одной создаваемой карте влияет на порядок в следующих картах. Делается это для того, чтобы карты можно было быстро сравнивать.

2> Men = #{boris=>2,carl=>3,alex=>1}.
#{boris => 2,carl => 3,alex => 1}
3> Husbands = #{alex=>1,boris=>2,carl=>3}.
#{boris => 2,carl => 3,alex => 1}
4> Men = Husbands.
#{boris => 2,carl => 3,alex => 1}
5> Guests = #{boris=>2,carl=>3,dima=>4,alex=>1}.
#{boris => 2,carl => 3,alex => 1,dima => 4}

Оператор :=

Создавать из одной карты другую можно, как говорилось выше, с помощью функции maps:put/3. Но этого же можно добиться и штатными возможностями языка. Для этого есть следующая форма:

NewMap = OldMap#{K1 Op V1, ... , Kn Op Vn}

Вместо Op может быть или уже знакомый нам оператор =>, или оператор :=.

При создании одной карты из другой возможно две ситуации:

Оператор => сработает в обоих случаях. Есть ключ или его нет, в новой карте появится новый элемент. Оператор := сработает только в том случае, если ключ задаваемого элемента есть в предшественнице. В противном случае будет крах.

1> Guests = #{boris=>2,carl=>3,dima=>4,alex=>1}.
#{boris => 2,carl => 3,dima => 4,alex => 1}
2> Guests#{alex=>0, egor=>5}.
#{boris => 2,carl => 3,dima => 4,alex => 0,egor => 5}
3> Guests#{alex:=0}.
#{boris => 2,carl => 3,dima => 4,alex => 0}
4> Guests#{egor:=5}.
** exception error: bad key: egor

Если мы создаём карту, используя предшественницу, при этом набор ключей останется таким же, то хорошей практикой будет всегда использовать := для обновления. Во-первых, это хорошее средство против опечаток в коде. Если случайно мы вместо username используем ключ user_name, при обновлении будет крах, который укажет нам на ошибку. Во-вторых, это отличная гарантия тому, что в разных картах (а в Эрланге может быть, например, список из миллиона карт) имеется один и тот же набор ключей. Значит, этот набор можно хранить в одной переменной.

Документация

maps — документация по модулю maps из стандартной библиотеки. ordsets — по модулю ordsets, предназначенному для работы с упорядоченными картами.


© Алексей Карманов, 2024.