std.range.primitives

Переместиться к: back · ElementEncodingType · ElementType · empty · front · hasAssignableElements · hasLength · hasLvalueElements · hasMobileElements · hasSlicing · hasSwappableElements · isBidirectionalRange · isForwardRange · isInfinite · isInputRange · isOutputRange · isRandomAccessRange · moveAt · moveBack · moveFront · popBack · popBackExactly · popBackN · popFront · popFrontExactly · popFrontN · put · save · walkLength

Этот модуль является подмодулем std.range.
Он предоставляет базовую функциональность диапазонов, определяя несколько шаблонов для анализа, является ли данный объект диапазоном, и какого типа диапазоном:
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).

Исходный код: std/range/primitives.d

Лицензия:
Boost License 1.0.
Авторы:
Andrei Alexandrescu, David Simcha, and Jonathan M Davis. Credit for some of the ideas in building this module goes to Leonardo Maffi.
enum bool isInputRange(R);
Возвращает true, если R является входным диапазоном. Входной дипазон должен определить примитивы empty, popFront, и front. Следующий код должен скомпилироваться для любого входного диапазона.
R r;              // можно определять объект диапазона
if (r.empty) {}   // можно тестировать на пустоту
r.popFront();     // можно вызвать popFront()
auto h = r.front; // можно получить передний элемент диапазона не-void типа
Ниже приведены правила входных диапазонов, которые предполагаются выполненными во всем коде в 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) или "дешевые" с точки зрения времени работы. Утверждения Ο() в документации для функций, работающих с диапазонами, сделаны этим предположением.
Параметры:
R тип для тестирования
Возвращает:
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();                               // можно вызвать 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))); // одинаковый тип для доступа по индексу и через front
static assert(!isNarrowString!R); // узкие строки не индексируются как диапазоны
static assert(hasLength!R || isInfinite!R); // должен иметь длину или быть бесконечным

// $ должно работать как с массивами, если opIndex работает с $
static if (is(typeof(r[$])))
{
    static assert(is(typeof(f) == typeof(r[$])));

    //  $ - 1 не имеет смысла с бесконечными диапазонами,
    // но должно работать с конечными.
    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[]));
template ElementType(R)
Тип элемента 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));

// Вызов .front извлекает декодированный dchar
static assert(is(ElementType!(char[])  == dchar)); // rvalue
static assert(is(ElementType!(dchar[]) == dchar)); // lvalue

// То же самое
static assert(is(ElementType!(string) == dchar));
static assert(is(ElementType!(dstring) == immutable(dchar)));

// Для диапазонов он получает тип .front.
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)));
enum bool hasLength(R);
Возвращает 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));
template isInfinite(R)
Возвращает true, если R является бесконечным входным диапазоном. Бесконечный входной диапазон является входным диапазоном, в котором статически определён член-перечисление (enum) с именем empty, который всегда равен false, например:
struct MyInfiniteRange
{
    enum bool empty = false;
    ...
}
Примеры:
import std.range : Repeat;
static assert(!isInfinite!(int[]));
static assert( isInfinite!(Repeat!(int)));
enum bool hasSlicing(R);
Возвращает 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);

// define a perfunctory input range
struct InputRange
{
    enum bool empty = false;
    enum int front = 7;
    void popFront() {}
    int moveFront() { return 43; }
}
InputRange r;
// calls r.moveFront
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);