std.container
В этом модуле определены обобщенные контейнеры.
- Создание
- Для реализации различных контейнеров были использованы подходы, основанные как на структурах, так и на классах.
std.container.util.makeпозволяет создавать контейнеры единообразно с использованием любого из этих подходов.
import std.container; // Construct a red-black tree and an array both containing the values 1, 2, 3. // RedBlackTree should typically be allocated using `new` RedBlackTree!int rbTree = new RedBlackTree!int(1, 2, 3); // But `new` should not be used with Array Array!int array = Array!int(1, 2, 3); // `make` hides the differences RedBlackTree!int rbTree2 = make!(RedBlackTree!int)(1, 2, 3); Array!int array2 = make!(Array!int)(1, 2, 3);Обратите внимание, что
make может вывести тип элемента из заданных аргументов. import std.container;
auto rbTree = make!RedBlackTree(1, 2, 3); // RedBlackTree!int
auto array = make!Array("1", "2", "3"); // Array!string
- Семантика ссылок
- Все контейнеры имеют семантику ссылок, что означает, что после присваивания обе переменные ссылаются на одни и те же данные.
c.dup. import std.container, std.range; Array!int originalArray = make!(Array!int)(1, 2, 3); Array!int secondArray = originalArray; assert(equal(originalArray[], secondArray[])); // changing one instance changes the other one as well! originalArray[0] = 12; assert(secondArray[0] == 12); // secondArray now refers to an independent copy of originalArray secondArray = originalArray.dup; secondArray[0] = 1; // assert that originalArray has not been affected assert(originalArray[0] == 12);Внимание: Если контейнер реализован как класс, использование неинициализированного экземпляра может привести к сбою из-за обращения к нулевому указателю.
import std.container; RedBlackTree!int rbTree; rbTree.insert(5); // null pointer dereferenceИспользование неинициализированного контейнера, основанного на структуре, будет работать, поскольку структура инициализируется при использовании; однако, до этого момента контейнер не будет иметь идентичности, а присвоение не создаст две ссылки на одни и те же данные.
import std.container; // create an uninitialized array Array!int array1; // array2 does _not_ refer to array1 Array!int array2 = array1; array2.insertBack(42); // thus array1 will not be affected assert(array1.empty); // after initialization reference semantics work as expected array1 = array2; // now affects array2 as well array1.removeBack(); assert(array2.empty);Поэтому рекомендуется всегда создавать контейнеры с помощью
std.container.util.make. Это, в действительности, необходимо для размещения контейнеров в другом контейнере. Например, чтобы создать Array из десяти пустых Array, используйте следующее, которое вызывает make десять раз. import std.container, std.range; auto arrOfArrs = make!Array(generate!(() => make!(Array!int)).take(10));
- Подмодули
- Этот модуль состоит из следующих подмодулей:
- Модуль
std.container.arrayпредоставляет тип массива с детерминированным управлением памятью, не зависящий от сборщика мусора, в отличие от встроенных массивов. - Модуль
std.container.binaryheapпредоставляет реализацию бинарной кучи, которую можно применять к любому предоставленному пользователем диапазону с произвольным доступом. - Модуль
std.container.dlistпредоставляет реализацию двусвязного списка. - Модуль
std.container.rbtreeреализует красно-черные деревья. - Модуль
std.container.slistреализует односвязные списки. - Модуль
std.container.utilсодержит некоторые общие инструменты, обычно используемые реализациями контейнеров.
- Основной диапазон контейнера
- Хотя некоторые контейнеры предлагают прямой доступ к своим элементам, например, через
opIndex,c.frontилиc.back, доступ к содержимому контейнера и его модификация обычно осуществляется через его основной тип диапазона, который алиасируется какC.Range. Например, основной тип диапазонаArray!int—Array!int.Range.
Range, то это относится к основному типу диапазона этого контейнера. Часто используется Take!Range, в этом случае диапазон относится к отрезку элементов в контейнере. Аргументы этих параметров должны быть получены от того же экземпляра контейнера, с которым выполняется работа. Важно отметить, что многие обобщенные алгоритмы диапазонов возвращают тот же тип диапазона, что и их входной диапазон. import std.algorithm.comparison : equal; import std.algorithm.iteration : find; import std.container; import std.range : take; auto array = make!Array(1, 2, 3); // `find` returns an Array!int.Range advanced to the element "2" array.linearRemove(array[].find(2)); assert(array[].equal([1])); array = make!Array(1, 2, 3); // the range given to `linearRemove` is a Take!(Array!int.Range) // spanning just the element "2" array.linearRemove(array[].find(2).take(1)); assert(array[].equal([1, 3]));Когда любой диапазон может быть передан в качестве аргумента в членскую функцию, в документации тип параметра обычно обозначается как
Stuff. import std.algorithm.comparison : equal; import std.container; import std.range : iota; auto array = make!Array(1, 2); // the range type returned by `iota` is completely unrelated to Array, // which is fine for Array.insertBack: array.insertBack(iota(3, 10)); assert(array[].equal([1, 2, 3, 4, 5, 6, 7, 8, 9]));
- Примитивы контейнера
- Контейнеры не образуют иерархии классов, вместо этого они реализуют общий набор примитивов (см. таблицу ниже). Каждый из этих примитивов гарантирует определенную сложность в худшем случае, что позволяет писать обобщенный код независимо от реализации контейнера.
c.remove(r) и c.linearRemove(r) оба удаляют последовательность элементов в диапазоне r из контейнера c. Примитив c.remove(r) гарантирует сложность Ο(nr log nc) в худшем случае, а c.linearRemove(r) ослабляет это ограничение до Ο(nc). Поскольку последовательность элементов может быть удалена из двусвязного списка в постоянное время, DList предоставляет примитив c.remove(r) и c.linearRemove(r). С другой стороны, массив предлагает только c.linearRemove(r). В таблице ниже описывается общий набор примитивов, которые реализуют контейнеры. Контейнер необязательно должен реализовывать все примитивы, но если примитив реализован, он должен поддерживать синтаксис, описанный в столбце синтаксис, с семантикой, описанной в столбце описание, и он не должен иметь сложность в худшем случае хуже, чем обозначено в нотации "большое О" в столбце Ο(·). Ниже, C означает тип контейнера, c — значение типа контейнера, nx представляет собой эффективную длину значения x, которое может быть одним элементом (в этом случае nx равно 1), контейнером или диапазоном. | Синтаксис | Ο(·) | Описание |
|---|---|---|
C(x) | nx | Создаёт контейнер типа C из другого контейнера или диапазона. Созданный контейнер не должен быть нулевым даже если x пустой. |
c.dup | nc | Возвращает копию контейнера. |
c ~ x | nc + nx | Возвращает конкатенацию c и r. x может быть отдельным элементом или диапазоном ввода. |
x ~ c | nc + nx | Возвращает конкатенацию x и c. x может быть отдельным элементом или диапазоном ввода. |
| Итерация | ||
c.Range | Основной тип диапазона, связанный с контейнером. | |
c[] | log nc | Возвращает диапазон, итерирующий по всему контейнеру в определённом контейнером порядке. |
c[a .. b] | log nc | Извлекает часть контейнера с ключом a до ключа b. |
| Емкость | ||
c.empty | 1 | Возвращает true, если контейнер пустой, false в противном случае. |
c.length | log nc | Возвращает количество элементов в контейнере. |
c.length = n | nc + n | Принудительно устанавливает количество элементов в контейнере на n. Если контейнер увеличивается, добавленные элементы инициализируются способом, зависящим от контейнера (обычно с использованием T.init). |
c.capacity | log nc | Возвращает максимальное количество элементов, которое может храниться в контейнере без вызова перераспределения. |
c.reserve(x) | nc | Принудительно устанавливает capacity как минимум на x без уменьшения. |
| Доступ | ||
c.front | log nc | Возвращает первый элемент контейнера в определённом контейнером порядке. |
c.moveFront | log nc | Деструктивно считывает и возвращает первый элемент контейнера. Ячейка не удаляется из контейнера; она инициализируется значением T.init. Эта процедура может не быть определена, если front возвращает ref. |
c.front = v | log nc | Присваивает v первому элементу контейнера. |
c.back | log nc | Возвращает последний элемент контейнера в определённом контейнером порядке. |
c.moveBack | log nc | Деструктивно считывает и возвращает последний элемент контейнера. Ячейка не удаляется из контейнера; она инициализируется значением T.init. Эта процедура может не быть определена, если front возвращает ref. |
c.back = v | log nc | Присваивает v последнему элементу контейнера. |
c[x] | log nc | Обеспечивает индексированный доступ к контейнеру. Тип индекса определяется контейнером. Контейнер может определять несколько типов индексов (и, следовательно, перегружать индексирование). |
c.moveAt(x) | log nc | Деструктивно считывает и возвращает значение в позиции x. Ячейка не удаляется из контейнера; она инициализируется значением T.init. |
c[x] = v | log nc | Устанавливает элемент в контейнер по указанному индексу. |
c[x] op= v | log nc | Выполняет операцию чтения-модификации-записи в контейнере по указанному индексу. |
| Операции | ||
e in c | log nc | Возвращает ненулевое значение, если e найдено в c. |
c.lowerBound(v) | log nc | Возвращает диапазон всех элементов, строго меньших v. |
c.upperBound(v) | log nc | Возвращает диапазон всех элементов, строго больших v. |
c.equalRange(v) | log nc | Возвращает диапазон всех элементов в c, равных v. |
| Измененные значения | ||
c ~= x | nc + nx | Добавляет x в c. x может быть отдельным элементом или диапазоном ввода. |
c.clear() | nc | Удаляет все элементы в c. |
c.insert(x) | nx * log nc | Вставляет x в c в позиции(и), выбранной c. |
c.stableInsert(x) | nx * log nc | То же, что и c.insert(x), но гарантирует, что никакие диапазоны не будут повреждены. |
c.linearInsert(v) | nc | То же, что и c.insert(v), но сложность снижена до линейной. |
c.stableLinearInsert(v) | nc | То же, что и c.stableInsert(v), но сложность снижена до линейной. |
c.removeAny() | log nc | Удаляет некоторый элемент из c и возвращает его. |
c.stableRemoveAny() | log nc | То же, что и c.removeAny(), но гарантирует, что никакие итераторы не будут повреждены. |
c.insertFront(v) | log nc | Вставляет v в начало c. |
c.stableInsertFront(v) | log nc | То же, что и c.insertFront(v), но гарантирует, что никакие диапазоны не будут повреждены. |
c.insertBack(v) | log nc | Вставляет v в конец c. |
c.stableInsertBack(v) | log nc | То же, что и c.insertBack(v), но гарантирует, что никакие диапазоны не будут повреждены. |
c.removeFront() | log nc | Удаляет элемент в начале c. |
c.stableRemoveFront() | log nc | То же, что и c.removeFront(), но гарантирует, что никакие диапазоны не будут повреждены. |
c.removeBack() | log nc | Удаляет значение в конце c. |
c.stableRemoveBack() | log nc | То же, что и c.removeBack(), но гарантирует, что никакие диапазоны не будут повреждены. |
c.remove(r) | nr * log nc | Удаляет диапазон r из c. |
c.stableRemove(r) | nr * log nc | То же, что и c.remove(r), но гарантирует, что итераторы не будут повреждены. |
c.linearRemove(r) | nc | Удаляет диапазон r из c. |
c.stableLinearRemove(r) | nc | То же, что и c.linearRemove(r), но гарантирует, что итераторы не будут повреждены. |
c.removeKey(k) | log nc | Удаляет элемент из c по ключу k. Тип ключа определяется контейнером. |
- Источник
- std/container/package.d
- Лицензия:
- Распространяется по лицензии Boost Software License, версия 1.0. (См. прилагаемый файл LICENSE_1_0.txt или копию по адресу boost.org/LICENSE_1_0.txt).
- Авторы:
- Steven Schveighoffer, Andrei Alexandrescu
© 1999–2021 The D Language Foundation
Licensed under the Boost License 1.0.
https://dlang.org/phobos/std_container.html