Он предоставляет базовую функциональность диапазонов, определяя несколько шаблонов для анализа, является ли данный объект диапазоном, и какого типа диапазоном:
isInputRange |
Тестирует, является ли что-то входным диапазоном, определяемым как что-то, с чего можно последовательно считывать данные с помощью
примитивов front, popFront и empty.
|
isOutputRange |
Тестирует, является ли что-то выходным диапазоном, определяемым как что-то, на что можно последовательно записывать данные, используя примитив
put.
|
isForwardRange |
Тестирует, является ли что-то лидирующим диапазоном, определяемым как входной диапазон с дополнительной возможностью, позволяющей сохранить текущую позицию с помощью примитива save, таким образом позволяя итерировать над одним диапазоном несколько раз.
|
isBidirectionalRange |
Тестирует, является ли что-то двунаправленным диапазоном, то есть, лидирующим диапазоном, который допускает обратное прохождение, используя примитивы back и popBack.
|
isRandomAccessRange |
Тестирует, является ли что-то диапазоном с произвольным доступом, являющимся двунаправленным диапазоном, который также поддерживает операцию индексации массива через примитив opIndex.
|
Он также предоставляет ряд шаблонов, которые тестируют наличие различных свойств диапазона:
hasMobileElements |
Тестирует, можно ли перемещать элементы данного диапазона с использованием примитивов moveFront, moveBack, или moveAt.
|
ElementType |
Возвращает тип элемента данного диапазона.
|
ElementEncodingType |
Возвращает тип кодирования элемента данного дипазона.
|
hasSwappableElements |
Тестирует, является ли диапазон лидирующим диапазоном со сменяемыми элементами.
|
hasAssignableElements |
Тестирует, является ли диапазон лидирующим диапазоном с изменяемыми элементами.
|
hasLvalueElements |
Тестирует, является ли диапазон лидирующим диапазоном с элементами, которые можно передавать по ссылке, и у которых можно получать адрес.
|
hasLength |
Тестирует, имеет ли данный диапазон атрибут длины length.
|
isInfinite |
Тестирует, является ли данный диапазон бесконечным диапазоном.
|
hasSlicing |
Тестирует, поддерживает ли данный диапазон операцию получения среза массива R[x..y].
|
Наконец, он включает некоторые удобные функции для манипуляции диапазонами:
popFrontN |
Продвигается по данному диапазону на расстояние вплоть до n элементов.
|
popBackN |
Продвигается по данному двунаправленному диапазону справа на расстояние вплоть до
n элементов.
|
popFrontExactly |
Продвигается по данному диапазону точно на n элементов.
|
popBackExactly |
Продвигается по данному двунаправленному диапазону справа точно на
n элементов.
|
moveFront |
Удаляет передний элемент диапазона.
|
moveBack |
Удаляет задний элемент двунаправленного диапазона.
|
moveAt |
Возвращает i-й элемент диапазона с произвольным доступом.
|
walkLength |
Вычисляет длину любого диапазона за время O(n).
|
enum bool
isInputRange
(R);
Возвращает true
, если R является входным диапазоном. Входной дипазон должен определить примитивы empty, popFront, и front. Следующий код должен скомпилироваться для любого входного диапазона.
R r; if (r.empty) {} r.popFront(); auto h = r.front;
Ниже приведены правила входных диапазонов, которые предполагаются выполненными во всем коде в Phobos. Эти правила не контролируются во время компиляции, так что не соответствие этим правилам при написании диапазонов или кода, основанного на диапазонах, приведёт к неопределенному поведению.
- r.empty возвращает false тогда и только тогда, когда ещё есть данные, доступные в диапазоне.
- r.empty вычисляется множество раз, не вызывая
r.popFront, или иным способом видоизменяя объект диапазона или основные данные, и даёт одинаковый результат для каждого вычисления.
- r.front возвращает текущий элемент в диапазоне. Он может возвращать значение или ссылку.
- r.front можно легально вычислить тогда и только тогда, когда
r.empty равно, или будет равно false.
- r.front вычисляется множество раз, не вызывая
r.popFront, или иным способом видоизменяя объект диапазона или основные данные, и даёт одинаковый результат для каждого вычисления.
- r.popFront продвигает к следующему элементу в диапазоне.
- r.popFront можно вызвать тогда и только тогда, когда r.empty
равно, или будет равно false.
Также, заметьте, что код Phobos допускает, что примитивы
r.front и
r.empty имеют временную сложность
Ο(1) или "дешевые" с точки зрения времени работы. Утверждения
Ο() в документации для функций, работающих с диапазонами, сделаны этим предположением.
Возвращает: true
если R является входным диапазоном, false
если нет
Примеры: struct A {}
struct B
{
void popFront();
@property bool empty();
@property int front();
}
static assert(!isInputRange!A);
static assert( isInputRange!B);
static assert( isInputRange!(int[]));
static assert( isInputRange!(char[]));
static assert(!isInputRange!(char[4]));
static assert( isInputRange!(inout(int)[]));
void
put
(R, E)(ref R
r
, E
e
);
Выводит e
на r
. Точный эффект зависит от двух типов. Принимаются несколько случаев , как описано ниже. Производятся попытки применить фрагменты кода в порядке, и первый, который скомпилируется, "выигрывает" и будет применяться.
В этой таблице "doPut" – это метод, который устанавливает
e
в
r
, используя корректный примитив:
r
.put
(e
) если в
R определено
put
,
r
.front = e
если
r
– входной диапазон (с последующим
r
.popFront()), или
r
(e
)
в противном случае.
Фрагмент кода |
Сценарий |
r .doPut(e ); |
R специально принимает E. |
r .doPut([ e ]); |
R специально принимает E[]. |
r .putChar(e ); |
R принимает некоторую форму строки или символа. put будет перекодировать символ e соответственно. |
for (; !e .empty; e .popFront()) put (r , e .front); |
Копирование диапазона E в R. |
Подсказка:
put
нельзя использовать в "стиле UFCS", например r
.put
(e
).
Делая это, можно вызвать R.put
напрямую, обходя любую возможность преобразования, предусмотренную Range.put
. Предпочтительно использовать put
(r
, e
).
enum bool
isOutputRange
(R, E);
Возвращает true
, если R – выходной диапазон для элементов типа
E. Выходной дипазон определяется функционально как диапазон, который поддерживает операцию put(r, e), как определено выше.
Примеры: void myprint(in char[] s) { }
static assert(isOutputRange!(typeof(&myprint), char));
static assert(!isOutputRange!(char[], char));
static assert( isOutputRange!(dchar[], wchar));
static assert( isOutputRange!(dchar[], dchar));
enum bool
isForwardRange
(R);
Возвращает true
, если R – лидирующий диапазон. Лидирующий диапазон является входным диапазоном r который может сохранять "контрольные точки", сохраняя r.save
в другую величину типа R. Показательными примерами входных диапазонов, которые не являются лидирующими диапазонами являются диапазоны file/socket; копирование такого диапазона не сохранит позицию в потоке, и они, скорее всего, многократно используют внутренний буфер, так как поток целиком не сидит в памяти. Впоследствии, предоставление исходного диапазона или копии предоставит поток, так что копии не являются независимыми.
Следующий код должен компилироваться для любого лидирующего диапазона.
static assert(isInputRange!R);
R r1;
auto s1 = r1.save;
static assert (is(typeof(s1) == R));
Сохранение диапазона не дублирует его; в примере выше,
r1
и
r2 всё ещё ссылаются на одни и те же лежащие в основе данные. Они просто перемещаются по этим данным независимо.
Семантика лидирующего диапазона (не контролируемая во время компиляции), такая же, как и для входного диапазона, с дополнительным требованием, чтобы был возможен возврат в исходное состояние через сохранение копии объекта диапазона с помощью
save, и использование его позже.
Примеры: static assert(!isForwardRange!(int));
static assert( isForwardRange!(int[]));
static assert( isForwardRange!(inout(int)[]));
enum bool
isBidirectionalRange
(R);
Возвращает true
, если R – двунаправленный диапазон. Двунаправленный диапазон является лидирующим диапазоном, который также предлагает примитивы back и
popBack. Следующий код должен компилироваться для любого двунаправленного диапазона.
Семантика двунаправленного диапазона (не контролируемая во время компиляции) принимает следующее (
r – объект типа
R):
- r.back возвращает (возможно, как ссылку) последний элемент в диапазоне. Вызов r.back допускается, только если вызов
r.empty возвращает
false
.
Примеры: alias R = int[];
R r = [0,1];
static assert(isForwardRange!R); r.popBack(); auto t = r.back; auto w = r.front;
static assert(is(typeof(t) == typeof(w)));
enum bool
isRandomAccessRange
(R);
Возвращает true
, если R – диапазон с произвольным доступом. Диапазон с произвольным доступом является двунаправленным диапазоном, который также предлагает примитив opIndex, ИЛИ бесконечным лидирующим диапазоном, который предлагает opIndex. В любом случае, диапазон должен или иметь длину, или быть бесконечным. Следующий код должен компилироваться для любого диапазона с произвольным доступом.
Семантика диапазона с произвольным доступом (не контролируемая во время компиляции) принимает следующее (
r – объект типа
R):
- r.opIndex(n) возвращает ссылку на
n-ый элемент в диапазоне.
Хотя
char[] и
wchar[] (а также их ограниченные версии, включая
string и
wstring) являются массивами,
isRandomAccessRange
возвратит для них
false
, поскольку они используют переменную длину кодирования (UTF-8 и UTF-16 соответственно). Эти типы являются только двунаправленными диапазонами.
Примеры: alias R = int[];
static assert(isBidirectionalRange!R ||
isForwardRange!R && isInfinite!R);
R r = [0,1];
auto e = r[1]; auto f = r.front;
static assert(is(typeof(e) == typeof(f))); static assert(!isNarrowString!R); static assert(hasLength!R || isInfinite!R);
static if (is(typeof(r[$])))
{
static assert(is(typeof(f) == typeof(r[$])));
static if (!isInfinite!R)
static assert(is(typeof(f) == typeof(r[$ - 1])));
}
enum bool
hasMobileElements
(R);
Возвращает true
тогда и только тогда, когда R является входным диапазоном, который поддерживает примитив
moveFront, а также moveBack и moveAt если это – двунаправленный диапазон или диапазон с произвольным доступом. Они могут быть реализованы в явном виде, или могут работать через встроенное поведение функций уровня модуля moveFront
и друзей. Следующий код должен компилироваться для любого диапазона с мобильными элементами.
alias E = ElementType!R;
R r;
static assert(isInputRange!R);
static assert(is(typeof(moveFront(r)) == E));
static if (isBidirectionalRange!R)
static assert(is(typeof(moveBack(r)) == E));
static if (isRandomAccessRange!R)
static assert(is(typeof(moveAt(r, 0)) == E));
Примеры: import std.algorithm.iteration : map;
import std.range : iota, repeat;
static struct HasPostblit
{
this(this) {}
}
auto nonMobile = map!"a"(repeat(HasPostblit.init));
static assert(!hasMobileElements!(typeof(nonMobile)));
static assert( hasMobileElements!(int[]));
static assert( hasMobileElements!(inout(int)[]));
static assert( hasMobileElements!(typeof(iota(1000))));
static assert( hasMobileElements!( string));
static assert( hasMobileElements!(dstring));
static assert( hasMobileElements!( char[]));
static assert( hasMobileElements!(dchar[]));
Тип элемента R. R не обязан быть диапазоном. Тип элемента определяется как тип, получаемый в результате r.front для объекта r типа R. Например, ElementType
!(T[]) – это
T, если T[] не является узкой строкой; в этом случае тип элемента –
dchar. Если R не имеет метода front, ElementType
!R возвращает
void.
Примеры: import std.range : iota;
static assert(is(ElementType!(int[]) == int));
static assert(is(ElementType!(char[]) == dchar)); static assert(is(ElementType!(dchar[]) == dchar));
static assert(is(ElementType!(string) == dchar));
static assert(is(ElementType!(dstring) == immutable(dchar)));
auto range = iota(0, 10);
static assert(is(ElementType!(typeof(range)) == int));
template
ElementEncodingType
(R)
Кодирующий тип элемента R. Для узких строк (char[],
wchar[] и их ограниченных вариантов, включая string и
wstring), ElementEncodingType
– это тип символов строки. Для всех остальных типов, ElementEncodingType
– тоже самое, что и
ElementType.
Примеры: import std.range : iota;
static assert(is(ElementEncodingType!(char[]) == char));
static assert(is(ElementEncodingType!(wstring) == immutable(wchar)));
static assert(is(ElementEncodingType!(byte[]) == byte));
auto range = iota(0, 10);
static assert(is(ElementEncodingType!(typeof(range)) == int));
enum bool
hasSwappableElements
(R);
Возвращает true
, если R – входной диапазон и имеет сменяемые элементы. Следующий код должен компилироваться для любого дипазона со сменяемыми элементами.
R r;
static assert(isInputRange!R);
swap(r.front, r.front);
static if (isBidirectionalRange!R) swap(r.back, r.front);
static if (isRandomAccessRange!R) swap(r[], r.front);
Примеры: static assert(!hasSwappableElements!(const int[]));
static assert(!hasSwappableElements!(const(int)[]));
static assert(!hasSwappableElements!(inout(int)[]));
static assert( hasSwappableElements!(int[]));
static assert(!hasSwappableElements!( string));
static assert(!hasSwappableElements!(dstring));
static assert(!hasSwappableElements!( char[]));
static assert( hasSwappableElements!(dchar[]));
enum bool
hasAssignableElements
(R);
Возвращает true
, если R – входной диапазон и имеет изменяемые элементы. Следующий код должен компилироваться для любого диапазона с присваиваемыми элементами.
R r;
static assert(isInputRange!R);
r.front = r.front;
static if (isBidirectionalRange!R) r.back = r.front;
static if (isRandomAccessRange!R) r[0] = r.front;
Примеры: static assert(!hasAssignableElements!(const int[]));
static assert(!hasAssignableElements!(const(int)[]));
static assert( hasAssignableElements!(int[]));
static assert(!hasAssignableElements!(inout(int)[]));
static assert(!hasAssignableElements!( string));
static assert(!hasAssignableElements!(dstring));
static assert(!hasAssignableElements!( char[]));
static assert( hasAssignableElements!(dchar[]));
enum bool
hasLvalueElements
(R);
Тестирует, содержит ли диапазон
R lvalue-элементы. Они определяются как элементы, которые можно передать ссылкой и взять их адрес. Следующий код должен компилироваться для любого диапазона с lvalue-элементами.
void passByRef(ref ElementType!R stuff);
...
static assert(isInputRange!R);
passByRef(r.front);
static if (isBidirectionalRange!R) passByRef(r.back);
static if (isRandomAccessRange!R) passByRef(r[0]);
Примеры: import std.range : iota, chain;
static assert( hasLvalueElements!(int[]));
static assert( hasLvalueElements!(const(int)[]));
static assert( hasLvalueElements!(inout(int)[]));
static assert( hasLvalueElements!(immutable(int)[]));
static assert(!hasLvalueElements!(typeof(iota(3))));
static assert(!hasLvalueElements!( string));
static assert( hasLvalueElements!(dstring));
static assert(!hasLvalueElements!( char[]));
static assert( hasLvalueElements!(dchar[]));
auto c = chain([1, 2, 3], [4, 5, 6]);
static assert( hasLvalueElements!(typeof(c)));
Возвращает true
, если R содержит член length (длина), который возвращает целый тип. R не обязан быть диапазоном. Заметьте, что длина не является дополнительным примитивом, так как никакой диапазон не обязан её реализовывать. Некоторые диапазоны не хранят свою длину явно, некоторые не могут вычислить её без фактического исчерпания диапазона (например, socket streams), и некоторые другие диапазоны могут быть бесконечными.
Хотя типы узких строк (char[], wchar[], и их ограниченные производные) определяют свойство length, hasLength
возвращает для них false
. Дело в том, что длина узкой строки не отражает количество символов, вместо этого она показывает количество encoding units, и как таковая не является полезной с алгоритмами, ориентированными на диапазоны.
Примеры: static assert(!hasLength!(char[]));
static assert( hasLength!(int[]));
static assert( hasLength!(inout(int)[]));
struct A { ulong length; }
struct B { size_t length() { return 0; } }
struct C { @property size_t length() { return 0; } }
static assert( hasLength!(A));
static assert( hasLength!(B));
static assert( hasLength!(C));
Возвращает true
, если R является бесконечным входным диапазоном. Бесконечный входной диапазон является входным диапазоном, в котором статически определён член-перечисление (enum) с именем empty, который всегда равен false
,
например:
struct MyInfiniteRange
{
enum bool empty = false;
...
}
Примеры: import std.range : Repeat;
static assert(!isInfinite!(int[]));
static assert( isInfinite!(Repeat!(int)));
Возвращает true
, если R предлагает оператор выделения среза с целыми границами, который возвращает дипазон лидирующего типа.
Для конечных диапазонов, результат
opSlice должен быть того же самого типа, что и тип исходного диапазона. Если диапазон определяет свойство
opDollar, тогда он должен поддерживать вычитание.
Для бесконечных диапазонов, когда
не используется
opDollar, результат
opSlice должен быть результатом
take или
takeExactly в исходном диапазоне (они оба возвращают одинаковый тип для бесконечных диапазонов). Тем не менее, при использовании
opDollar, результат
opSlice должен быть того же самого типа, что и оригинальный диапазон.
Следующий код должен компилироваться, чтобы
hasSlicing
был истиной:
R r = void;
static if (isInfinite!R)
typeof(take(r, 1)) s = r[1 .. 2];
else
{
static assert(is(typeof(r[1 .. 2]) == R));
R s = r[1 .. 2];
}
s = r[1 .. 2];
static if (is(typeof(r[0 .. $])))
{
static assert(is(typeof(r[0 .. $]) == R));
R t = r[0 .. $];
t = r[0 .. $];
static if (!isInfinite!R)
{
static assert(is(typeof(r[0 .. $ - 1]) == R));
R u = r[0 .. $ - 1];
u = r[0 .. $ - 1];
}
}
static assert(isForwardRange!(typeof(r[1 .. 2])));
static assert(hasLength!(typeof(r[1 .. 2])));
Примеры: import std.range : takeExactly;
static assert( hasSlicing!(int[]));
static assert( hasSlicing!(const(int)[]));
static assert(!hasSlicing!(const int[]));
static assert( hasSlicing!(inout(int)[]));
static assert(!hasSlicing!(inout int []));
static assert( hasSlicing!(immutable(int)[]));
static assert(!hasSlicing!(immutable int[]));
static assert(!hasSlicing!string);
static assert( hasSlicing!dstring);
enum rangeFuncs = "@property int front();" ~
"void popFront();" ~
"@property bool empty();" ~
"@property auto save() { return this; }" ~
"@property size_t length();";
struct A { mixin(rangeFuncs); int opSlice(size_t, size_t); }
struct B { mixin(rangeFuncs); B opSlice(size_t, size_t); }
struct C { mixin(rangeFuncs); @disable this(); C opSlice(size_t, size_t); }
struct D { mixin(rangeFuncs); int[] opSlice(size_t, size_t); }
static assert(!hasSlicing!(A));
static assert( hasSlicing!(B));
static assert( hasSlicing!(C));
static assert(!hasSlicing!(D));
struct InfOnes
{
enum empty = false;
void popFront() {}
@property int front() { return 1; }
@property InfOnes save() { return this; }
auto opSlice(size_t i, size_t j) { return takeExactly(this, j - i); }
auto opSlice(size_t i, Dollar d) { return this; }
struct Dollar {}
Dollar opDollar() const { return Dollar.init; }
}
static assert(hasSlicing!InfOnes);
auto
walkLength
(Range)(Range
range
)
if (isInputRange!Range && !isInfinite!Range);
auto
walkLength
(Range)(Range
range
, const size_t
upTo
)
if (isInputRange!Range);
Это лучшая из возможных реализация расчета длины для любого типа диапазона.
Если
hasLength!Range, просто возвращается
range
.length без
проверки
upTo
(когда задана).
В противном случае, проходит через диапазон на всю длину и возвращает количество увиденных элементов. Выполняет
Ο(n) вычислений
range
.empty
и
range
.popFront(), где
n – эффективная длина диапазона
range
.
Параметр
upTo
полезен, чтобы "сократить потери" в случае, если интерес в том, чтобы узнать, имеет ли диапазон по крайней мере некоторое количество элементов. Если параметр
upTo
определён, процесс прекращается после
upTo
шагов, и возвращается
upTo
.
Бесконечные диапазоны совместимы, при условии, что задан параметр
upTo
, в этом случае реализовано простое возвращение
upTo
.
size_t
popFrontN
(Range)(ref Range
r
, size_t
n
)
if (isInputRange!Range);
size_t
popBackN
(Range)(ref Range
r
, size_t
n
)
if (isBidirectionalRange!Range);
Жадно продвигается по самому r
(не по копии) вплоть до n
раз (вызывая
r
.popFront). popFrontN
принимает r
через ref,
так что это влияет на исходный диапазон. Выполняется за Ο(1) шагов для диапазонов, которые поддерживают срезы и имеют длину.
Выполняется за время Ο(n
) для всех других диапазонов.
Возвращает: На сколько
r
на самом деле продвинулся, это может быть меньше, чем
n
, если в
r
не было по крайней мере
n
элементов.
popBackN
будет вести себя так же, но здесь элементы удаляются с обратной стороны диапазона (двунаправленного) вместо передней стороны.
Примеры: int[] a = [ 1, 2, 3, 4, 5 ];
a.popFrontN(2);
assert(a == [ 3, 4, 5 ]);
a.popFrontN(7);
assert(a == [ ]);
Примеры: import std.algorithm.comparison : equal;
import std.range : iota;
auto LL = iota(1L, 7L);
auto r = popFrontN(LL, 2);
assert(equal(LL, [3L, 4L, 5L, 6L]));
assert(r == 2);
Примеры: int[] a = [ 1, 2, 3, 4, 5 ];
a.popBackN(2);
assert(a == [ 1, 2, 3 ]);
a.popBackN(7);
assert(a == [ ]);
Примеры: import std.algorithm.comparison : equal;
import std.range : iota;
auto LL = iota(1L, 7L);
auto r = popBackN(LL, 2);
assert(equal(LL, [1L, 2L, 3L, 4L]));
assert(r == 2);
void
popFrontExactly
(Range)(ref Range
r
, size_t
n
)
if (isInputRange!Range);
void
popBackExactly
(Range)(ref Range
r
, size_t
n
)
if (isBidirectionalRange!Range);
Жадно продвигается по самому r
(не по копии) точно n
раз (вызывая r
.popFront). popFrontExactly
принимает r
через ref,
так что это влияет на исходный диапазон. Выполняется за Ο(1) шагов для диапазонов, которые поддерживают срезы и, или имеют длину, или являются бесконечными. Выполняется за время Ο(n
) для всех других диапазонов.
Замечание:
В отличие от popFrontN, popFrontExactly
допускает, что диапазон содержит по крайней мере n
элементов. Это делает popFrontExactly
быстрее, чем popFrontN, но это также означает, что если диапазон не содержит по крайней мере n
элементов, произойдёт попытка вызвать popFront
на пустом диапазоне, что приведёт к неопределенному поведению. Так что используйте
popFrontExactly
только тогда, когда гарантировано, что диапазон содержит по крайней мере
n
элементов.
popBackExactly
будет вести себя так же, но здесь элементы удаляются с обратной стороны диапазона (двунаправленного) вместо передней стороны.
Примеры: import std.algorithm.iteration : filterBidirectional;
import std.algorithm.comparison : equal;
auto a = [1, 2, 3];
a.popFrontExactly(1);
assert(a == [2, 3]);
a.popBackExactly(1);
assert(a == [2]);
string s = "日本語";
s.popFrontExactly(1);
assert(s == "本語");
s.popBackExactly(1);
assert(s == "本");
auto bd = filterBidirectional!"true"([1, 2, 3]);
bd.popFrontExactly(1);
assert(bd.equal([2, 3]));
bd.popBackExactly(1);
assert(bd.equal([2]));
ElementType!R
moveFront
(R)(R
r
);
Перемещает r
к началу и возвращает front
. Оставляет r
.front в разрушаемом состоянии, которое не выделяет каких-либо ресурсов (как правило, равно его .init значению).
Примеры: auto a = [ 1, 2, 3 ];
assert(moveFront(a) == 1);
assert(a.length == 3);
struct InputRange
{
enum bool empty = false;
enum int front = 7;
void popFront() {}
int moveFront() { return 43; }
}
InputRange r;
assert(moveFront(r) == 43);
ElementType!R
moveBack
(R)(R
r
);
Перемещает r
к концу и возвращает back. Оставляет r
.back в разрушаемом состоянии, которое не выделяет каких-либо ресурсов (как правило, равно его .init значению).
Примеры: struct TestRange
{
int payload = 5;
@property bool empty() { return false; }
@property TestRange save() { return this; }
@property ref int front() return { return payload; }
@property ref int back() return { return payload; }
void popFront() { }
void popBack() { }
}
static assert(isBidirectionalRange!TestRange);
TestRange r;
auto x = moveBack(r);
assert(x == 5);
ElementType!R
moveAt
(R)(R
r
, size_t
i
);
Перемещает r
к элементу с индексом i
и возвращает его. Оставляет r
.front в разрушаемом состоянии, которое не выделяет каких-либо ресурсов (как правило, равно его .init значению).
Примеры: auto a = [1,2,3,4];
foreach (idx, it; a)
{
assert(it == moveAt(a, idx));
}
pure nothrow @nogc @property @safe bool
empty
(T)(in T[]
a
);
Реализует примитив empty
интерфейса диапазона для встроенных массивов. В связи с тем, что внешние функции (nonmember functions) могут вызываться первым аргументом с использованием точечной нотации, array.empty
– это эквивалент empty
(array).
Примеры: auto a = [ 1, 2, 3 ];
assert(!a.empty);
assert(a[3 .. $].empty);
pure nothrow @nogc @property @safe T[]
save
(T)(T[]
a
);
Реализует примитив save
интерфейса диапазона для встроенных массивов. В связи с тем, что внешние функции (nonmember functions) могут вызываться первым аргументом с использованием точечной нотации, array.save
– это эквивалент save
(array). Функция не дублирует содержимое массива, она просто возвращает свой аргумент.
Примеры: auto a = [ 1, 2, 3 ];
auto b = a.save;
assert(b is a);
pure nothrow @nogc @safe void
popFront
(T)(ref T[]
a
)
if (!isNarrowString!(T[]) && !is(T[] == void[]));
pure nothrow @trusted void
popFront
(C)(ref C[]
str
)
if (isNarrowString!(C[]));
Реализует примитив
popFront
интерфейса диапазона для встроенных массивов. В связи с тем, что внешние функции (nonmember functions) могут вызываться первым аргументом с использованием точечной нотации,
array.popFront
– это эквивалент
popFront
(array). Для
узких строк,
popFront
автоматически продвигает в следующую
кодовую точку (code point).
Примеры: auto a = [ 1, 2, 3 ];
a.popFront();
assert(a == [ 2, 3 ]);
pure nothrow @nogc @safe void
popBack
(T)(ref T[]
a
)
if (!isNarrowString!(T[]) && !is(T[] == void[]));
pure @safe void
popBack
(T)(ref T[]
a
)
if (isNarrowString!(T[]));
Реализует примитив
popBack
интерфейса диапазона для встроенных массивов. В связи с тем, что внешние функции (nonmember functions) могут вызываться первым аргументом с использованием точечной нотации,
array.popBack
– это эквивалент
popBack
(array). Для
узких строк,
popBack автоматически удаляет последнюю
кодовую точку (code point).
Примеры: auto a = [ 1, 2, 3 ];
a.popBack();
assert(a == [ 1, 2 ]);
pure nothrow @nogc @property ref @safe T
front
(T)(T[]
a
)
if (!isNarrowString!(T[]) && !is(T[] == void[]));
pure @property @safe dchar
front
(T)(T[]
a
)
if (isNarrowString!(T[]));
Реализует примитив
front
интерфейса диапазона для встроенных массивов. В связи с тем, что внешние функции (nonmember functions) могут вызываться первым аргументом с использованием точечной нотации,
array.front
– это эквивалент
front
(array). Для
узких строк,
front
автоматически возвращает первую
кодовую точку (code point) в виде
dchar.
Примеры: int[] a = [ 1, 2, 3 ];
assert(a.front == 1);
pure nothrow @nogc @property ref @safe T
back
(T)(T[]
a
)
if (!isNarrowString!(T[]) && !is(T[] == void[]));
pure @property @safe dchar
back
(T)(T[]
a
)
if (isNarrowString!(T[]));
Реализует примитив
back
интерфейса диапазона для встроенных массивов. В связи с тем, что внешние функции (nonmember functions) могут вызываться первым аргументом с использованием точечной нотации,
array.back
– это эквивалент
back
(array). Для
узких строк,
back
автоматически возвращает последнюю
кодовую точку (code point) в виде
dchar.
Примеры: int[] a = [ 1, 2, 3 ];
assert(a.back == 3);
a.back += 4;
assert(a.back == 7);