Выбрать главу

Array (Массив) — линейный и изменяемый список элементов. Все значения будут иметь один тип, возможно, объединение.

Tuple (Кортеж) — линейный и неизменяемый список элементов, в котором точный тип каждого элемента сохраняется и известен во время компиляции.

Set (Набор) — уникальная и неупорядоченная группа элементов. Значения никогда не повторяются, и при перечислении значения отображаются в том порядке, в котором они были вставлены (без дубликатов).

Hash (Хэш) — уникальная коллекция пар ключ-значение. Значения можно получить по их ключам и перезаписать, обеспечивая уникальность ключей. Как и Set, он нумеруется в порядке вставки.

NamedTuple — неизменяемая коллекция пар ключ-значение, где каждый ключ известен во время компиляции, а также тип каждого значения.

Deque — изменяемый и упорядоченный список элементов, предназначенный для использования либо в виде структуры стека (FIFO, или First In First Out), либо в качестве структуры очереди (FILO, или First In Last Out). Он оптимизирован для быстрой вставки и удаления на обоих концах.

Далее давайте подробнее рассмотрим некоторые из этих типов контейнеров.

Массивы и кортежи

Вы можете выразить некоторые простые данные с помощью чисел и текста, но вам быстро понадобится собрать больше информации в списки. Для этого вы можете использовать массивы и кортежи. Массив — это динамический контейнер, который может увеличиваться, сжиматься и изменяться во время выполнения программы. С другой стороны, кортеж статичен и неизменяем; его размер и типы элементов известны и фиксируются во время компиляции:

numbers = [1, 2, 3, 4] # This is of type Array(Int32)

numbers << 10

puts "The #{numbers.size} numbers are #{numbers}"

   # => The 5 numbers are [1, 2, 3, 4, 10]

С массивами нельзя смешивать разные типы, если они не были указаны при создании массива. Эти ошибки обнаруживаются во время сборки; они не являются исключениями во время выполнения. Посмотрите это, например:

numbers << "oops"

   # Error: no overload matches 'Array(Int32)#<<' with type String

Используя типы объединения, вы можете иметь массивы, в которых сочетаются более одного типа, либо инициализируя их несколькими типами, либо явно указывая их. Вот пример:

first_list = [1, 2, 3, "abc", 40]

p typeof(first_list) # => Array(Int32 | String)

first_list << "hey!" # Ok

# Now all elements are unions:

element = first_list[0]

p element         # => 1

p element.class   # => Int32

p typeof(element) # => Int32 | String

# Types can also be explicit:

second_list = [1, 2, 3, 4] of Int32 | String

p typeof(second_list) # => Array(Int32 | String)

second_list << "hey!" # Ok

# When declaring an empty array, an explicit type is mandatory:

empty_list = [] of Int32

Внутри массива все значения имеют один и тот же тип; значения разных типов при необходимости расширяются до объединения типов или общего предка. Это важно, поскольку массивы изменяемы, и значение по заданному индексу можно свободно заменить чем-то другим.

Тип Array реализует стандартные модули Indexable, Enumerable и Iterable, предоставляя несколько полезных методов для исследования коллекции и управления ею.

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

list = {1, 2, "abc", 40}

p typeof(list) # => Tuple(Int32, Int32, String, Int32)

element = list[0]

p typeof(element) # => Int32

list << 10 # Invalid, tuples are immutable.

Поскольку кортежи неизменяемы, они используются не так часто, как массивы.

И массивы, и кортежи имеют несколько полезных методов. Вот некоторые из наиболее распространенных:

Таблица 2.7 – Общие операции с контейнерами Array и Tuple
Операция Описание
list [index] Считывает элемент по заданному индексу. Вызывает ошибку времени выполнения, если этот индекс выходит за пределы. Если список представляет собой кортеж, а индекс — целое число, ошибка выхода за пределы будет обнаружена во время компиляции.
list[index]? Аналогично list [index], но возвращает ni1, если индекс выходит за пределы.
list.size Возвращает количество элементов внутри кортежа или массива.
array[index] = value Заменяет значение по заданному индексу или повышает, если индекс выходит за пределы. Поскольку кортежи неизменяемы, это доступно только для массивов.
array << value array.push(value) Добавляет новое значение в конец массива, увеличивая его размер на единицу.
array.pop array.pop? Удаляет и возвращает последний элемент массива. В зависимости от варианта он может поднимать или возвращать ноль в пустых массивах.
array.shift array.shift? Аналогично pop, но удаляет и возвращает первый элемент массива, уменьшая его размер на единицу.
array.unshift(value) Добавляет новое значение в начало массива, увеличивая его размер на единицу. Это противоположность сдвигу.