• 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.
Поскольку кортежи неизменяемы, они используются не так часто, как массивы.
И массивы, и кортежи имеют несколько полезных методов. Вот некоторые из наиболее распространенных:
Операция | Описание |
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) | Добавляет новое значение в начало массива, увеличивая его размер на единицу. Это противоположность сдвигу. |