Диапазоны являются абстракцией доступа к элементам. Эта абстракция позволяет использовать большое количество алгоритмов для большого количества типов контейнеров. Диапазоны подчеркивают, как осуществляется доступ к элементам контейнера, а не к реализации контейнеров. Диапазоны — это очень простая концепция, основанная на том, определяет ли тип определенные наборы функций-членов.
Диапазоны являются неотъемлемой частью срезов D. D, как оказалось, являются реализациями самого мощного диапазона RandomAccessRange, и в Фобосе есть много функций диапазона. Многие алгоритмы Фобоса возвращают объекты временного диапазона. Например, filter () выбирает элементы, которые больше 10, в следующем коде фактически возвращает объект диапазона, а не массив.
Диапазоны номеров
Числовые диапазоны довольно часто используются, и эти числовые диапазоны имеют тип int. Несколько примеров для диапазонов номеров показаны ниже —
// Example 1 foreach (value; 3..7) // Example 2 int[] slice = array[5..10];
Фобосские хребты
Диапазоны, связанные со структурами и интерфейсами классов — это диапазоны Фобоса. Phobos — это официальная библиотека времени выполнения и стандартная библиотека, которая поставляется с компилятором языка D.
Существуют различные типы диапазонов, которые включают в себя —
- InputRange
- ForwardRange
- BidirectionalRange
- RandomAccessRange
- OutputRange
InputRange
Самый простой диапазон — это диапазон ввода. Другие диапазоны предъявляют больше требований к диапазону, на котором они основаны. Есть три функции, которые требует InputRange —
-
empty — указывает, пуст ли диапазон; он должен возвращать true, если диапазон считается пустым; ложь в противном случае.
-
front — обеспечивает доступ к элементу в начале диапазона.
-
popFront () — сокращает диапазон с начала, удаляя первый элемент.
empty — указывает, пуст ли диапазон; он должен возвращать true, если диапазон считается пустым; ложь в противном случае.
front — обеспечивает доступ к элементу в начале диапазона.
popFront () — сокращает диапазон с начала, удаляя первый элемент.
пример
import std.stdio; import std.string; struct Student { string name; int number; string toString() const { return format("%s(%s)", name, number); } } struct School { Student[] students; } struct StudentRange { Student[] students; this(School school) { this.students = school.students; } @property bool empty() const { return students.length == 0; } @property ref Student front() { return students[0]; } void popFront() { students = students[1 .. $]; } } void main() { auto school = School([ Student("Raj", 1), Student("John", 2), Student("Ram", 3)]); auto range = StudentRange(school); writeln(range); writeln(school.students.length); writeln(range.front); range.popFront; writeln(range.empty); writeln(range); }
Когда приведенный выше код компилируется и выполняется, он дает следующий результат —
[Raj(1), John(2), Ram(3)] 3 Raj(1) false [John(2), Ram(3)]
ForwardRange
ForwardRange дополнительно требует части функции-члена сохранения из трех других функций InputRange и возвращает копию диапазона при вызове функции сохранения.
import std.array; import std.stdio; import std.string; import std.range; struct FibonacciSeries { int first = 0; int second = 1; enum empty = false; // infinite range @property int front() const { return first; } void popFront() { int third = first + second; first = second; second = third; } @property FibonacciSeries save() const { return this; } } void report(T)(const dchar[] title, const ref T range) { writefln("%s: %s", title, range.take(5)); } void main() { auto range = FibonacciSeries(); report("Original range", range); range.popFrontN(2); report("After removing two elements", range); auto theCopy = range.save; report("The copy", theCopy); range.popFrontN(3); report("After removing three more elements", range); report("The copy", theCopy); }
Когда приведенный выше код компилируется и выполняется, он дает следующий результат —
Original range: [0, 1, 1, 2, 3] After removing two elements: [1, 2, 3, 5, 8] The copy: [1, 2, 3, 5, 8] After removing three more elements: [5, 8, 13, 21, 34] The copy: [1, 2, 3, 5, 8]
BidirectionalRange
BidirectionalRange дополнительно предоставляет две функции-члена над функциями-членами ForwardRange. Задняя функция, аналогичная передней, обеспечивает доступ к последнему элементу диапазона. Функция popBack похожа на функцию popFront и удаляет последний элемент из диапазона.
пример
import std.array; import std.stdio; import std.string; struct Reversed { int[] range; this(int[] range) { this.range = range; } @property bool empty() const { return range.empty; } @property int front() const { return range.back; // reverse } @property int back() const { return range.front; // reverse } void popFront() { range.popBack(); } void popBack() { range.popFront(); } } void main() { writeln(Reversed([ 1, 2, 3])); }
Когда приведенный выше код компилируется и выполняется, он дает следующий результат —
[3, 2, 1]
Infinite RandomAccessRange
opIndex () дополнительно требуется по сравнению с ForwardRange. Кроме того, значение пустой функции, которая будет известна во время компиляции как false. Простой пример поясняется диапазоном квадратов, который показан ниже.
import std.array; import std.stdio; import std.string; import std.range; import std.algorithm; class SquaresRange { int first; this(int first = 0) { this.first = first; } enum empty = false; @property int front() const { return opIndex(0); } void popFront() { ++first; } @property SquaresRange save() const { return new SquaresRange(first); } int opIndex(size_t index) const { /* This function operates at constant time */ immutable integerValue = first + cast(int)index; return integerValue * integerValue; } } bool are_lastTwoDigitsSame(int value) { /* Must have at least two digits */ if (value < 10) { return false; } /* Last two digits must be divisible by 11 */ immutable lastTwoDigits = value % 100; return (lastTwoDigits % 11) == 0; } void main() { auto squares = new SquaresRange(); writeln(squares[5]); writeln(squares[10]); squares.popFrontN(5); writeln(squares[0]); writeln(squares.take(50).filter!are_lastTwoDigitsSame); }
Когда приведенный выше код компилируется и выполняется, он дает следующий результат —
25 100 25 [100, 144, 400, 900, 1444, 1600, 2500]
Finite RandomAccessRange
opIndex () и длина дополнительно требуются при сравнении с двунаправленным диапазоном. Это объясняется с помощью подробного примера, который использует ряды Фибоначчи и пример квадратов, использованный ранее. Этот пример хорошо работает на обычном D-компиляторе, но не работает на онлайн-компиляторе.
пример
import std.array; import std.stdio; import std.string; import std.range; import std.algorithm; struct FibonacciSeries { int first = 0; int second = 1; enum empty = false; // infinite range @property int front() const { return first; } void popFront() { int third = first + second; first = second; second = third; } @property FibonacciSeries save() const { return this; } } void report(T)(const dchar[] title, const ref T range) { writefln("%40s: %s", title, range.take(5)); } class SquaresRange { int first; this(int first = 0) { this.first = first; } enum empty = false; @property int front() const { return opIndex(0); } void popFront() { ++first; } @property SquaresRange save() const { return new SquaresRange(first); } int opIndex(size_t index) const { /* This function operates at constant time */ immutable integerValue = first + cast(int)index; return integerValue * integerValue; } } bool are_lastTwoDigitsSame(int value) { /* Must have at least two digits */ if (value < 10) { return false; } /* Last two digits must be divisible by 11 */ immutable lastTwoDigits = value % 100; return (lastTwoDigits % 11) == 0; } struct Together { const(int)[][] slices; this(const(int)[][] slices ...) { this.slices = slices.dup; clearFront(); clearBack(); } private void clearFront() { while (!slices.empty && slices.front.empty) { slices.popFront(); } } private void clearBack() { while (!slices.empty && slices.back.empty) { slices.popBack(); } } @property bool empty() const { return slices.empty; } @property int front() const { return slices.front.front; } void popFront() { slices.front.popFront(); clearFront(); } @property Together save() const { return Together(slices.dup); } @property int back() const { return slices.back.back; } void popBack() { slices.back.popBack(); clearBack(); } @property size_t length() const { return reduce!((a, b) => a + b.length)(size_t.init, slices); } int opIndex(size_t index) const { /* Save the index for the error message */ immutable originalIndex = index; foreach (slice; slices) { if (slice.length > index) { return slice[index]; } else { index -= slice.length; } } throw new Exception( format("Invalid index: %s (length: %s)", originalIndex, this.length)); } } void main() { auto range = Together(FibonacciSeries().take(10).array, [ 777, 888 ], (new SquaresRange()).take(5).array); writeln(range.save); }
Когда приведенный выше код компилируется и выполняется, он дает следующий результат —
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 777, 888, 0, 1, 4, 9, 16]
OutputRange
OutputRange представляет потоковый вывод элемента, аналогично отправке символов в стандартный вывод. OutputRange требует поддержки операции put (range, element). put () — это функция, определенная в модуле std.range. Он определяет возможности диапазона и элемента во время компиляции и использует наиболее подходящий метод для вывода элементов. Простой пример показан ниже.
import std.algorithm; import std.stdio; struct MultiFile { string delimiter; File[] files; this(string delimiter, string[] fileNames ...) { this.delimiter = delimiter; /* stdout is always included */ this.files ~= stdout; /* A File object for each file name */ foreach (fileName; fileNames) { this.files ~= File(fileName, "w"); } } void put(T)(T element) { foreach (file; files) { file.write(element, delimiter); } } } void main() { auto output = MultiFile("\n", "output_0", "output_1"); copy([ 1, 2, 3], output); copy([ "red", "blue", "green" ], output); }
Когда приведенный выше код компилируется и выполняется, он дает следующий результат —