Spec-Zone.ru › D

std.range

В этом модуле определено понятие диапазона. Диапазоны обобщают понятие массивов, списков или чего угодно, что предполагает последовательный доступ. Эта абстракция позволяет использовать один и тот же набор алгоритмов (см. std.algorithm) с огромным разнообразием различных конкретных типов. Например, алгоритм линейного поиска, такой как std.algorithm.searching.find, работает не только для массивов, но и для связанных списков, входных файлов, входящих сетевых данных и т. д.

Руководства
Существует множество статей, которые помогут лучше понять диапазоны:
  • Учебник Али Чеhreli's по диапазонам для основ работы с диапазонами и создания кода, ориентированного на них.
  • Выступление Джонатана М. Дэвиса Вступление к диапазонам на DConf 2015 — яркое введение от основных понятий до практических советов.
  • Глава учебника DLang Tour по диапазонам для интерактивного знакомства.
  • Учебник H. S. Teoh по компонентному программированию с использованием диапазонов для демонстрации реального применения влияния программирования с использованием диапазонов на сложные алгоритмы.
  • Статья Андреа Александреску Об итерации для концептуального понимания диапазонов и мотивации
Подмодули
Этот модуль содержит два подмодуля:
Подмодуль std.range.primitives предоставляет базовые функциональные возможности для работы с диапазонами. Он определяет несколько шаблонов для проверки того, является ли данный объект диапазоном, какой это диапазон, и предоставляет некоторые общие операции с диапазонами. Подмодуль std.range.interfaces предоставляет интерфейсы на основе объектов для работы с диапазонами с помощью полиморфизма во время выполнения. Остальная часть модуля предоставляет богатый набор шаблонов для создания и композиции диапазонов, позволяющих создавать новые диапазоны из существующих:
chain Объединяет несколько диапазонов в один диапазон.
choose Выбирает один из двух диапазонов во время выполнения на основе булевого условия.
chooseAmong Выбирает один из нескольких диапазонов во время выполнения на основе индекса.
chunks Создает диапазон, возвращающий куски заданного размера из исходного диапазона.
cycle Создает бесконечный диапазон, повторяющий заданный диапазон вперед бесконечно. Подходит для реализации циклических буферов.
drop Создает диапазон, полученный из заданного диапазона путём отбрасывания первых n элементов.
dropBack Создает диапазон, полученный из заданного диапазона путём отбрасывания последних n элементов.
dropExactly Создает диапазон, полученный из заданного диапазона путём отбрасывания ровно n первых элементов.
dropBackExactly Создает диапазон, полученный из заданного диапазона путём отбрасывания ровно n последних элементов.
dropOne Создает диапазон, полученный из заданного диапазона путём отбрасывания первого элемента.
dropBackOne Создает диапазон, полученный из заданного диапазона путём отбрасывания последнего элемента.
enumerate Итерирует диапазон с присоединённой переменной индекса.
evenChunks Создаёт диапазон, возвращающий куски примерно равной длины из исходного диапазона.
frontTransversal Создаёт диапазон, итерирующий по первым элементам заданных диапазонов.
generate Создаёт диапазон последовательными вызовами заданной функции. Это позволяет создавать диапазоны как единый делегат.
indexed Создаёт диапазон, представляющий заданный диапазон так, как будто его элементы были переупорядочены в соответствии с заданным диапазоном индексов.
iota Создаёт диапазон, состоящий из чисел между начальной и конечной точкой, с заданным интервалом.
lockstep Итерирует n диапазонов синхронно, для использования в цикле foreach. Похоже на zip, за исключением того, что lockstep предназначен специально для циклов foreach.
nullSink Диапазон вывода, который отбрасывает получаемые данные.
only Создаёт диапазон, итерирующий по заданным аргументам.
padLeft Заполняет диапазон до заданной длины, добавляя заданный элемент в начало диапазона. Ленивый, если длина диапазона известна.
padRight Лениво заполняет диапазон до заданной длины, добавляя заданный элемент в конец диапазона.
radial Принимая диапазон с произвольным доступом и начальную точку, создаёт диапазон, который поочерёдно возвращает следующий левый и следующий правый элемент относительно начальной точки.
recurrence Создаёт диапазон вперед, значения которого определяются рекуррентным соотношением.
refRange Передаёт диапазон по ссылке. И исходный диапазон, и RefRange всегда будут содержать точно такие же элементы. Любая операция, выполненная с одним из них, повлияет на другой.
repeat Создаёт диапазон, состоящий из одного элемента, повторяющегося n раз, или бесконечный диапазон, бесконечно повторяющий этот элемент.
retro Итерирует двунаправленный диапазон в обратном порядке.
roundRobin Принимая n диапазонов, создаёт новый диапазон, возвращающий по очереди первые n элементов каждого диапазона, затем второй элемент каждого диапазона и так далее по кругу.
sequence Аналогично recurrence, за исключением того, что создаётся диапазон с произвольным доступом.
slide Создаёт диапазон, возвращающий слайдинг-окно фиксированного размера над исходным диапазоном. В отличие от кусков, он перемещается на определённое количество элементов за раз, а не на один кусок за раз.
stride Итерирует диапазон с шагом n.
tail Возвращает диапазон, сдвинутый на расстояние до n элементов от конца заданного диапазона.
take Создаёт поддиапазон, содержащий только первые n элементов заданного диапазона.
takeExactly Как take, но предполагает, что заданный диапазон фактически содержит n элементов, и поэтому также определяет свойство length.
takeNone Создаёт диапазон с произвольным доступом, содержащий ноль элементов заданного диапазона.
takeOne Создаёт диапазон с произвольным доступом, содержащий ровно первый элемент заданного диапазона.
tee Создаёт диапазон, который оборачивает заданный диапазон, передавая его элементы, а также вызывая предоставленную функцию с каждым элементом.
transposed Транспонирует диапазон диапазонов.
transversal Создаёт диапазон, итерирующий по n-му элементу заданных диапазонов с произвольным доступом.
zip Принимая n диапазонов, создаёт диапазон, последовательно возвращающий кортеж всех первых элементов, кортеж всех вторых элементов и так далее.
Отсортированность
Диапазоны, элементы которых отсортированы, обеспечивают лучшую эффективность при выполнении определённых операций. Для этого функция assumeSorted может быть использована для построения SortedRange из предварительно отсортированного диапазона. Функция std.algorithm.sorting.sort также удобно возвращает SortedRange. Объекты SortedRange предоставляют некоторые дополнительные операции с диапазонами, которые используют тот факт, что диапазон отсортирован.
Исходный код
std/range/package.d
Лицензия:
Лицензия Boost 1.0.
Авторы:
Андрей Александреску, Дэвид Симча, Джонатан М. Дэвис и Джек Стауффер. Часть идей для создания этого модуля принадлежит Леонардо Маффи.
auto retro(Range)(Range r)
Constraints: if (isBidirectionalRange!(Unqual!Range));

Итерирует двунаправленный диапазон в обратном порядке. Исходный диапазон можно получить, используя свойство source. Применение retro дважды к одному и тому же диапазону возвращает исходный диапазон.

Параметры:
Range r двунаправленный диапазон для итерации в обратном порядке
Возвращает:
Двунаправленный диапазон с длиной, если r также предоставляет длину. В противном случае, если r является диапазоном произвольного доступа, возвращаемое значение также будет диапазоном произвольного доступа.
См. также:
std.algorithm.mutation.reverse для изменения исходного диапазона напрямую.
Примеры:
import std.algorithm.comparison : equal;
int[5] a = [ 1, 2, 3, 4, 5 ];
int[5] b = [ 5, 4, 3, 2, 1 ];
assert(equal(retro(a[]), b[]));
assert(retro(a[]).source is a[]);
assert(retro(retro(a[])) is a[]);
auto stride(Range)(Range r, size_t n)
Constraints: if (isInputRange!(Unqual!Range));

Итерирует диапазон r с шагом n. Если диапазон является диапазоном произвольного доступа, перемещение происходит путем индексирования в диапазон; в противном случае, перемещение происходит последовательными вызовами popFront. Применение stride дважды к одному и тому же диапазону приводит к шагу, являющемуся произведением двух применений. Ошибка возникает, если n равно 0.

Параметры:
Range r входной диапазон для итерирования с шагом
size_t n количество элементов, которые нужно пропустить
Возвращает:
По крайней мере, входной диапазон. Результирующий диапазон примет диапазонные примитивы базового диапазона, при условии, что std.range.primitives.hasLength равно true.
Примеры:
import std.algorithm.comparison : equal;

int[] a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ];
assert(equal(stride(a, 3), [ 1, 4, 7, 10 ][]));
writeln(stride(stride(a, 2), 3)); // stride(a, 6)
auto chain(Ranges...)(Ranges rs)
Constraints: if (Ranges.length > 0 && allSatisfy!(isInputRange, staticMap!(Unqual, Ranges)) && !is(CommonType!(staticMap!(ElementType, staticMap!(Unqual, Ranges))) == void));

Последовательно охватывает несколько диапазонов. Функция chain принимает любое количество диапазонов и возвращает объект Chain!(R1, R2,...). Диапазоны могут быть различными, но должны иметь одинаковый тип элементов. Результат — диапазон, который поддерживает примитивы front, popFront, и empty. Если все входные диапазоны поддерживают произвольный доступ и length, то Chain также поддерживает их.

Если функции Chain или chain предоставляется только один диапазон, тип Chain исчезает, так как он становится непосредственно алиасом типа этого диапазона.

Параметры:
Ranges rs входные диапазоны для объединения
Возвращает:
По крайней мере, входной диапазон. Если все диапазоны в rs предоставляют диапазонный примитив, возвращаемый диапазон также будет поддерживать этот примитив.
См. также:
only для объединения значений с диапазоном
Примеры:
import std.algorithm.comparison : equal;

int[] arr1 = [ 1, 2, 3, 4 ];
int[] arr2 = [ 5, 6 ];
int[] arr3 = [ 7 ];
auto s = chain(arr1, arr2, arr3);
writeln(s.length); // 7
writeln(s[5]); // 6
assert(equal(s, [1, 2, 3, 4, 5, 6, 7][]));
Примеры:
Диапазонные примитивы переносятся в возвращаемый диапазон, если все диапазоны их предоставляют
import std.algorithm.comparison : equal;
import std.algorithm.sorting : sort;

int[] arr1 = [5, 2, 8];
int[] arr2 = [3, 7, 9];
int[] arr3 = [1, 4, 6];

// in-place sorting across all of the arrays
auto s = arr1.chain(arr2, arr3).sort;

assert(s.equal([1, 2, 3, 4, 5, 6, 7, 8, 9]));
assert(arr1.equal([1, 2, 3]));
assert(arr2.equal([4, 5, 6]));
assert(arr3.equal([7, 8, 9]));
Примеры:
Из-за безопасного повышения типа в D, объединение различных символьных диапазонов приводит к диапазону типа uint. Используйте byChar, byWchar и byDchar для диапазонов, чтобы получить необходимый тип.
import std.utf : byChar, byCodeUnit;

auto s1 = "string one";
auto s2 = "string two";
// s1 and s2 front is dchar because of auto-decoding
static assert(is(typeof(s1.front) == dchar) && is(typeof(s2.front) == dchar));

auto r1 = s1.chain(s2);
// chains of ranges of the same character type give that same type
static assert(is(typeof(r1.front) == dchar));

auto s3 = "string three".byCodeUnit;
static assert(is(typeof(s3.front) == immutable char));
auto r2 = s1.chain(s3);
// chaining ranges of mixed character types gives `dchar`
static assert(is(typeof(r2.front) == dchar));

// use byChar on character ranges to correctly convert them to UTF-8
auto r3 = s1.byChar.chain(s3);
static assert(is(typeof(r3.front) == immutable char));
auto choose(R1, R2)(bool condition, return scope R1 r1, return scope R2 r2)
Constraints: if (isInputRange!(Unqual!R1) && isInputRange!(Unqual!R2) && !is(CommonType!(ElementType!(Unqual!R1), ElementType!(Unqual!R2)) == void));

Выбирает один из двух диапазонов во время выполнения в зависимости от булевого условия.

Диапазоны могут быть различными, но должны иметь совместимые типы элементов (т.е. CommonType должен существовать для двух типов элементов). Результатом является диапазон, который поддерживает наименьшие возможности из двух (например, ForwardRange если R1 — диапазон произвольного доступа, а R2 — диапазон прямого доступа).

Параметры:
bool condition который диапазон выбрать: r1 если true, r2 в противном случае
R1 r1 диапазон «истина»
R2 r2 диапазон «ложь»
Возвращает:
Тип диапазона, зависящий от R1 и R2.
Примеры:
import std.algorithm.comparison : equal;
import std.algorithm.iteration : filter, map;

auto data1 = only(1, 2, 3, 4).filter!(a => a != 3);
auto data2 = only(5, 6, 7, 8).map!(a => a + 1);

// choose() is primarily useful when you need to select one of two ranges
// with different types at runtime.
static assert(!is(typeof(data1) == typeof(data2)));

auto chooseRange(bool pickFirst)
{
    // The returned range is a common wrapper type that can be used for
    // returning or storing either range without running into a type error.
    return choose(pickFirst, data1, data2);

    // Simply returning the chosen range without using choose() does not
    // work, because map() and filter() return different types.
    //return pickFirst ? data1 : data2; // does not compile
}

auto result = chooseRange(true);
assert(result.equal(only(1, 2, 4)));

result = chooseRange(false);
assert(result.equal(only(6, 7, 8, 9)));
auto chooseAmong(Ranges...)(size_t index, return scope Ranges rs)
Constraints: if (Ranges.length >= 2 && allSatisfy!(isInputRange, staticMap!(Unqual, Ranges)) && !is(CommonType!(staticMap!(ElementType, Ranges)) == void));

Выбирает один из нескольких диапазонов во время выполнения.

Диапазоны могут быть различными, но должны иметь совместимые типы элементов. Результатом является диапазон, который поддерживает наименьшие возможности из всех Ranges.

Параметры:
size_t index который диапазон выбрать, должно быть меньше количества диапазонов
Ranges rs два или более диапазонов
Возвращает:
Индексированный диапазон. Если rs состоит только из одного диапазона, возвращаемый тип — алиас типа этого диапазона.
Примеры:
auto test()
{
    import std.algorithm.comparison : equal;

    int[4] sarr1 = [1, 2, 3, 4];
    int[2] sarr2 = [5, 6];
    int[1] sarr3 = [7];
    auto arr1 = sarr1[];
    auto arr2 = sarr2[];
    auto arr3 = sarr3[];

    {
        auto s = chooseAmong(0, arr1, arr2, arr3);
        auto t = s.save;
        writeln(s.length); // 4
        writeln(s[2]); // 3
        s.popFront();
        assert(equal(t, only(1, 2, 3, 4)));
    }
    {
        auto s = chooseAmong(1, arr1, arr2, arr3);
        writeln(s.length); // 2
        s.front = 8;
        assert(equal(s, only(8, 6)));
    }
    {
        auto s = chooseAmong(1, arr1, arr2, arr3);
        writeln(s.length); // 2
        s[1] = 9;
        assert(equal(s, only(8, 9)));
    }
    {
        auto s = chooseAmong(1, arr2, arr1, arr3)[1 .. 3];
        writeln(s.length); // 2
        assert(equal(s, only(2, 3)));
    }
    {
        auto s = chooseAmong(0, arr1, arr2, arr3);
        writeln(s.length); // 4
        writeln(s.back); // 4
        s.popBack();
        s.back = 5;
        assert(equal(s, only(1, 2, 5)));
        s.back = 3;
        assert(equal(s, only(1, 2, 3)));
    }
    {
        uint[5] foo = [1, 2, 3, 4, 5];
        uint[5] bar = [6, 7, 8, 9, 10];
        auto c = chooseAmong(1, foo[], bar[]);
        writeln(c[3]); // 9
        c[3] = 42;
        writeln(c[3]); // 42
        writeln(c.moveFront()); // 6
        writeln(c.moveBack()); // 10
        writeln(c.moveAt(4)); // 10
    }
    {
        import std.range : cycle;
        auto s = chooseAmong(0, cycle(arr2), cycle(arr3));
        assert(isInfinite!(typeof(s)));
        assert(!s.empty);
        writeln(s[100]); // 8
        writeln(s[101]); // 9
        assert(s[0 .. 3].equal(only(8, 9, 8)));
    }
    return 0;
}
// works at runtime
auto a = test();
// and at compile time
static b = test();
auto roundRobin(Rs...)(Rs rs)
Constraints: if (Rs.length > 1 && allSatisfy!(isInputRange, staticMap!(Unqual, Rs)));

roundRobin(r1, r2, r3) возвращает r1.front, затем r2.front, затем r3.front, после чего извлекает по одному элементу из каждого и продолжает с r1. Например, при использовании двух диапазонов, он поочередно возвращает элементы из двух диапазонов. roundRobin останавливается после того, как все диапазоны будут исчерпаны (пропуская диапазоны, которые закончатся раньше).

Примеры:
import std.algorithm.comparison : equal;

int[] a = [ 1, 2, 3 ];
int[] b = [ 10, 20, 30, 40 ];
auto r = roundRobin(a, b);
assert(equal(r, [ 1, 10, 2, 20, 3, 30, 40 ]));
Примеры:
roundRobin можно использовать для создания функциональности «перемежения», которая вставляет элемент между каждым элементом в диапазоне.
import std.algorithm.comparison : equal;

auto interleave(R, E)(R range, E element)
if ((isInputRange!R && hasLength!R) || isForwardRange!R)
{
    static if (hasLength!R)
        immutable len = range.length;
    else
        immutable len = range.save.walkLength;

    return roundRobin(
        range,
        element.repeat(len - 1)
    );
}

assert(interleave([1, 2, 3], 0).equal([1, 0, 2, 0, 3]));
auto radial(Range, I)(Range r, I startingIndex)
Constraints: if (isRandomAccessRange!(Unqual!Range) && hasLength!(Unqual!Range) && hasSlicing!(Unqual!Range) && isIntegral!I);

auto radial(R)(R r)
Constraints: if (isRandomAccessRange!(Unqual!R) && hasLength!(Unqual!R) && hasSlicing!(Unqual!R));

Итерирует диапазон произвольного доступа, начиная с заданной точки и постепенно расширяясь влево и вправо от этой точки. Если начальная точка не указана, итерирование начинается с середины диапазона. Итерирование охватывает весь диапазон.

Когда startingIndex равно 0, диапазон будет итерироваться в прямом порядке, а когда r.length задан — в обратном порядке.

Параметры:
Range r диапазон произвольного доступа с длиной и нарезкой
I startingIndex индекс для начала итерации
Возвращает:
Диапазон прямого доступа с длиной
Примеры:
import std.algorithm.comparison : equal;
int[] a = [ 1, 2, 3, 4, 5 ];
assert(equal(radial(a), [ 3, 4, 2, 5, 1 ]));
a = [ 1, 2, 3, 4 ];
assert(equal(radial(a), [ 2, 3, 1, 4 ]));

// If the left end is reached first, the remaining elements on the right
// are concatenated in order:
a = [ 0, 1, 2, 3, 4, 5 ];
assert(equal(radial(a, 1), [ 1, 2, 0, 3, 4, 5 ]));

// If the right end is reached first, the remaining elements on the left
// are concatenated in reverse order:
assert(equal(radial(a, 4), [ 4, 5, 3, 2, 1, 0 ]));
Take!R take(R)(R input, size_t n)
Constraints: if (isInputRange!(Unqual!R));

struct Take(Range) if (isInputRange!(Unqual!Range) && !(!isInfinite!(Unqual!Range) && hasSlicing!(Unqual!Range) || is(Range T == Take!T)));

template Take(R) if (isInputRange!(Unqual!R) && (!isInfinite!(Unqual!R) && hasSlicing!(Unqual!Range) || is(R T == Take!T)))

Лениво берет только до n элементов диапазона. Это особенно полезно при работе с бесконечными диапазонами.

В отличие от takeExactly, take не требует, чтобы в n было input или больше элементов. Вследствие этого, информация о длине не применяется к результату, если input также не содержит информацию о длине.

Параметры:
R input входной диапазон для итерации не более n раз
size_t n количество элементов для взятия
Возвращает:
По крайней мере, входной диапазон. Если диапазон поддерживает произвольный доступ и length, take также поддерживает их.
Примеры:
import std.algorithm.comparison : equal;

int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
auto s = take(arr1, 5);
writeln(s.length); // 5
writeln(s[4]); // 5
assert(equal(s, [ 1, 2, 3, 4, 5 ][]));
Примеры:
Если диапазон исчерпывается до n элементов, take просто возвращает весь диапазон (в отличие от takeExactly, который вызовет ошибку утверждения, если диапазон закончится преждевременно):
import std.algorithm.comparison : equal;

int[] arr2 = [ 1, 2, 3 ];
auto t = take(arr2, 5);
writeln(t.length); // 3
assert(equal(t, [ 1, 2, 3 ]));
auto takeExactly(R)(R range, size_t n)
Constraints: if (isInputRange!R);

Аналогично take, но предполагает, что range имеет по меньшей мере n элементов. Соответственно, результат takeExactly(range, n) всегда определяет свойство length (и инициализирует его значением n) даже в случаях, когда range сам не определяет length.

Результат takeExactly идентичен результату take в случаях, когда исходный диапазон определяет length или является бесконечным.

В отличие от take, однако, передача диапазона с менее чем n элементами в takeExactly является ошибкой; это приведет к ошибке утверждения.

Примеры:
import std.algorithm.comparison : equal;

auto a = [ 1, 2, 3, 4, 5 ];

auto b = takeExactly(a, 3);
assert(equal(b, [1, 2, 3]));
static assert(is(typeof(b.length) == size_t));
writeln(b.length); // 3
writeln(b.front); // 1
writeln(b.back); // 3
auto takeOne(R)(R source)
Constraints: if (isInputRange!R);

Возвращает диапазон с не более чем одним элементом; например, takeOne([42, 43, 44]) возвращает диапазон, состоящий из целого числа 42. Вызов popFront() для этого диапазона делает его пустым.

По сути, takeOne(r) несколько эквивалентен take(r, 1), но в определенных интерфейсах важно статически знать, что диапазон может содержать не более одного элемента.

Тип, возвращаемый takeOne, — это диапазон с произвольным доступом и длиной независимо от возможностей R, если это диапазон с однонаправленным доступом. (ещё одна особенность, отличающая takeOne от take). Если (D R) — это диапазон с однонаправленным доступом, но не диапазон с однонаправленным доступом, тип возвращаемого значения — диапазон с однонаправленным доступом со всеми возможностями произвольного доступа, за исключением сохранения.

Примеры:
auto s = takeOne([42, 43, 44]);
static assert(isRandomAccessRange!(typeof(s)));
writeln(s.length); // 1
assert(!s.empty);
writeln(s.front); // 42
s.front = 43;
writeln(s.front); // 43
writeln(s.back); // 43
writeln(s[0]); // 43
s.popFront();
writeln(s.length); // 0
assert(s.empty);
auto takeNone(R)()
Constraints: if (isInputRange!R);

Возвращает пустой диапазон, который статически известен как пустой и гарантированно будет иметь length и будет иметь произвольный доступ независимо от возможностей R.

Примеры:
auto range = takeNone!(int[])();
writeln(range.length); // 0
assert(range.empty);
auto takeNone(R)(R range)
Constraints: if (isInputRange!R);

Создаёт пустой диапазон из заданного диапазона за Ο(1). Если это возможно, он вернёт тот же тип диапазона. В противном случае он вернёт takeExactly(range, 0).

Примеры:
import std.algorithm.iteration : filter;
assert(takeNone([42, 27, 19]).empty);
assert(takeNone("dlang.org").empty);
assert(takeNone(filter!"true"([42, 27, 19])).empty);
auto tail(Range)(Range range, size_t n)
Constraints: if (isInputRange!Range && !isInfinite!Range && (hasLength!Range || isForwardRange!Range));

Возвращает диапазон, смещённый на _n элементов от конца range.

Предназначен в качестве диапазонного эквивалента утилиты Unix tail. Когда длина range меньше или равна _n, range возвращается как есть.

Завершается за Ο(1) шагов для диапазонов, которые поддерживают нарезку и имеют длину. Завершается за Ο(range.length) времени для всех остальных диапазонов.

Параметры:
Range range диапазон, из которого необходимо получить хвост
size_t n максимальное количество элементов, включаемых в хвост
Возвращает:
Возвращает хвост range, дополненный информацией о длине.
Примеры:
// tail -c n
writeln([1, 2, 3].tail(1)); // [3]
writeln([1, 2, 3].tail(2)); // [2, 3]
writeln([1, 2, 3].tail(3)); // [1, 2, 3]
writeln([1, 2, 3].tail(4)); // [1, 2, 3]
writeln([1, 2, 3].tail(0).length); // 0

// tail --lines=n
import std.algorithm.comparison : equal;
import std.algorithm.iteration : joiner;
import std.exception : assumeWontThrow;
import std.string : lineSplitter;
assert("one\ntwo\nthree"
    .lineSplitter
    .tail(2)
    .joiner("\n")
    .equal("two\nthree")
    .assumeWontThrow);
R drop(R)(R range, size_t n)
Constraints: if (isInputRange!R);

R dropBack(R)(R range, size_t n)
Constraints: if (isBidirectionalRange!R);

Функция-обёртка, которая вызывает std.range.primitives.popFrontN(range, n) и возвращает range. drop упрощает извлечение элементов из диапазона и последующую передачу его в другую функцию в одном выражении, в то время как popFrontN потребовало бы нескольких инструкций.

dropBack обеспечивает ту же функциональность, но вместо этого вызывает std.range.primitives.popBackN(range, n).

Примечание
drop и dropBack извлекут не более n элементов, но остановятся, если диапазон окажется пустым. В других языках это иногда называют skip.
Параметры:
R range диапазон с однонаправленным доступом для извлечения элементов
size_t n количество элементов для извлечения
Возвращает:
range с исключёнными до n элементов
См. также:
std.range.primitives.popFront, std.range.primitives.popBackN
Примеры:
import std.algorithm.comparison : equal;

writeln([0, 2, 1, 5, 0, 3].drop(3)); // [5, 0, 3]
writeln("hello world".drop(6)); // "world"
assert("hello world".drop(50).empty);
assert("hello world".take(6).drop(3).equal("lo "));
Примеры:
import std.algorithm.comparison : equal;

writeln([0, 2, 1, 5, 0, 3].dropBack(3)); // [0, 2, 1]
writeln("hello world".dropBack(6)); // "hello"
assert("hello world".dropBack(50).empty);
assert("hello world".drop(4).dropBack(4).equal("o w"));
R dropExactly(R)(R range, size_t n)
Constraints: if (isInputRange!R);

R dropBackExactly(R)(R range, size_t n)
Constraints: if (isBidirectionalRange!R);

Аналогично drop и dropBack но они вызывают range.popFrontExactly(n) и range.popBackExactly(n) вместо этого.

Примечание
В отличие от drop, dropExactly предполагает, что диапазон содержит не менее n элементов. Это делает dropExactly быстрее, чем drop, но также означает, что если range не содержит не менее n элементов, он попытается вызвать popFront на пустом диапазоне, что является неопределённым поведением. Поэтому используйте popFrontExactly только в том случае, если гарантируется, что range содержит не менее n элементов.
Параметры:
R range диапазон с однонаправленным доступом для извлечения элементов
size_t n количество элементов для извлечения
Возвращает:
range с удалёнными n элементами
См. также:
std.range.primitives.popFrontExcatly, std.range.primitives.popBackExcatly
Примеры:
import std.algorithm.comparison : equal;
import std.algorithm.iteration : filterBidirectional;

auto a = [1, 2, 3];
writeln(a.dropExactly(2)); // [3]
writeln(a.dropBackExactly(2)); // [1]

string s = "日本語";
writeln(s.dropExactly(2)); // "語"
writeln(s.dropBackExactly(2)); // "日"

auto bd = filterBidirectional!"true"([1, 2, 3]);
assert(bd.dropExactly(2).equal([3]));
assert(bd.dropBackExactly(2).equal([1]));
R dropOne(R)(R range)
Constraints: if (isInputRange!R);

R dropBackOne(R)(R range)
Constraints: if (isBidirectionalRange!R);

Функция-обёртка, которая вызывает range.popFront() и возвращает range. dropOne упрощает извлечение элемента из диапазона и последующую передачу его в другую функцию в одном выражении, в то время как popFront потребовало бы нескольких инструкций.

dropBackOne обеспечивает ту же функциональность, но вместо этого вызывает range.popBack().

Примеры:
import std.algorithm.comparison : equal;
import std.algorithm.iteration : filterBidirectional;
import std.container.dlist : DList;

auto dl = DList!int(9, 1, 2, 3, 9);
assert(dl[].dropOne().dropBackOne().equal([1, 2, 3]));

auto a = [1, 2, 3];
writeln(a.dropOne()); // [2, 3]
writeln(a.dropBackOne()); // [1, 2]

string s = "日本語";
import std.exception : assumeWontThrow;
assert(assumeWontThrow(s.dropOne() == "本語"));
assert(assumeWontThrow(s.dropBackOne() == "日本"));

auto bd = filterBidirectional!"true"([1, 2, 3]);
assert(bd.dropOne().equal([2, 3]));
assert(bd.dropBackOne().equal([1, 2]));
struct Repeat(T);

Repeat!T repeat(T)(T value);

Take!(Repeat!T) repeat(T)(T value, size_t n);

Создаёт диапазон, повторяющий одно значение.

Параметры:
T value значение для повторения
size_t n количество повторений value
Возвращает:
Если n не определено, бесконечный диапазон с произвольным доступом и нарезкой. Если n определено, диапазон с произвольным доступом и нарезкой.
Примеры:
import std.algorithm.comparison : equal;

assert(5.repeat().take(4).equal([5, 5, 5, 5]));
Примеры:
import std.algorithm.comparison : equal;

assert(5.repeat(4).equal([5, 5, 5, 5]));
inout @property inout(T) front();

inout @property inout(T) back();

enum bool empty;

void popFront();

void popBack();

inout @property auto save();

inout inout(T) opIndex(size_t);

auto opSlice(size_t i, size_t j);

enum auto opDollar;

inout auto opSlice(size_t, DollarToken);

Примитивы диапазонов

auto generate(Fun)(Fun fun)
Constraints: if (isCallable!fun);

auto generate(alias fun)()
Constraints: if (isCallable!fun);

Принимая вызываемую (std.traits.isCallable) функцию fun, создаёт диапазон, чей передний элемент определяется последовательными вызовами fun(). Это особенно полезно для вызова функций с глобальными побочными эффектами (случайные функции) или для создания диапазонов, представленных как один делегат, а не всей структурой front/popFront/empty. fun может принимать либо параметр шаблона-алиаса (существующая функция, делегат, тип структуры, определяющей static opCall), либо аргумент времени выполнения (делегат, объект-функция). Результирующий диапазон моделирует диапазон с однонаправленным доступом (std.range.primitives.isInputRange). Полученный диапазон вызовет fun() при создании и каждый раз при вызове popFront, а кэшированное значение будет возвращено при вызове front.

Возвращает:
диапазон inputRange, где каждый элемент представляет собой другой вызов функции.
Примеры:
import std.algorithm.comparison : equal;
import std.algorithm.iteration : map;

int i = 1;
auto powersOfTwo = generate!(() => i *= 2)().take(10);
assert(equal(powersOfTwo, iota(1, 11).map!"2^^a"()));
Примеры:
import std.algorithm.comparison : equal;

//Returns a run-time delegate
auto infiniteIota(T)(T low, T high)
{
    T i = high;
    return (){if (i == high) i = low; return i++;};
}
//adapted as a range.
assert(equal(generate(infiniteIota(1, 4)).take(10), [1, 2, 3, 1, 2, 3, 1, 2, 3, 1]));
Примеры:
import std.format : format;
import std.random : uniform;

auto r = generate!(() => uniform(0, 6)).take(10);
format("%(%s %)", r);
struct Cycle(R) if (isForwardRange!R && !isInfinite!R);

template Cycle(R) if (isInfinite!R)

struct Cycle(R) if (isStaticArray!R);

auto cycle(R)(R input)
Constraints: if (isInputRange!R);

Cycle!R cycle(R)(R input, size_t index = 0)
Constraints: if (isRandomAccessRange!R && !isInfinite!R);

@system Cycle!R cycle(R)(ref R input, size_t index = 0)
Constraints: if (isStaticArray!R);

Повторяет заданный диапазон в прямом направлении бесконечно. Если исходный диапазон бесконечен (факт, который сделает Cycle тождественным отображением), Cycle обнаруживает это и меняет своё имя на тип самого диапазона. Это работает и для непрямых диапазонов. Если исходный диапазон имеет произвольный доступ, Cycle предоставляет произвольный доступ, а также конструктор, принимающий начальную позицию index. Cycle работает со статическими массивами в дополнение к диапазонам, в основном по причинам производительности.

Примечание
Входной диапазон не должен быть пустым.
Совет
Это отличный способ реализовать простые кольцевые буферы.
Примеры:
import std.algorithm.comparison : equal;
import std.range : cycle, take;

// Here we create an infinitive cyclic sequence from [1, 2]
// (i.e. get here [1, 2, 1, 2, 1, 2 and so on]) then
// take 5 elements of this sequence (so we have [1, 2, 1, 2, 1])
// and compare them with the expected values for equality.
assert(cycle([1, 2]).take(5).equal([ 1, 2, 1, 2, 1 ]));
this(R input, size_t index = 0);

@property ref auto front();

const @property ref auto front();

@property void front(ElementType!R val);

enum bool empty;

void popFront();

ref auto opIndex(size_t n);

const ref auto opIndex(size_t n);

void opIndexAssign(ElementType!R val, size_t n);

@property Cycle save();

enum auto opDollar;

auto opSlice(size_t i, size_t j);

auto opSlice(size_t i, DollarToken);

Примитивы диапазонов

struct Zip(Ranges...) if (Ranges.length && allSatisfy!(isInputRange, Ranges));

auto zip(Ranges...)(Ranges ranges)
Constraints: if (Ranges.length && allSatisfy!(isInputRange, Ranges));

auto zip(Ranges...)(StoppingPolicy sp, Ranges ranges)
Constraints: if (Ranges.length && allSatisfy!(isInputRange, Ranges));

Проход по нескольким диапазонам синхронно. Тип элемента — это прокси-кортеж, позволяющий обращаться к текущему элементу в n-ом диапазоне, используя e[n].

zip аналогичен lockstep, но lockstep не объединяет свои элементы и использует протокол opApply. lockstep позволяет получать доступ к элементам по ссылке в foreach итерациях.

Параметры:
StoppingPolicy sp управляет тем, что zip будет делать, если диапазоны имеют разную длину
Ranges ranges диапазоны для объединения
Возвращает:
По меньшей мере, входной диапазон. Zip предлагает минимальные возможности диапазонов всех компонентов, например, он предлагает произвольный доступ, если все диапазоны предлагают произвольный доступ, а также предлагает мутацию и обмен, если все диапазоны это предлагают. Благодаря этому, Zip чрезвычайно мощный, потому что он позволяет манипулировать несколькими диапазонами синхронно.
Исключения:
Исключение Exception, если все диапазоны не имеют одинаковую длину, и sp установлено в StoppingPolicy.requireSameLength.
Ограничения
Атрибуты @nogc и nothrow не могут быть определены для структуры Zip, потому что StoppingPolicy может изменяться во время выполнения. Это ограничение не распространяется на анонимный диапазон, возвращаемый функцией zip, когда явно не указано StoppingPolicy в качестве аргумента.
Примеры:
import std.algorithm.comparison : equal;
import std.algorithm.iteration : map;

// pairwise sum
auto arr = only(0, 1, 2);
auto part1 = zip(arr, arr.dropOne).map!"a[0] + a[1]";
assert(part1.equal(only(1, 3)));
Примеры:
import std.conv : to;

int[] a = [ 1, 2, 3 ];
string[] b = [ "a", "b", "c" ];
string[] result;

foreach (tup; zip(a, b))
{
    result ~= tup[0].to!string ~ tup[1];
}

writeln(result); // ["1a", "2b", "3c"]

size_t idx = 0;
// unpacking tuple elements with foreach
foreach (e1, e2; zip(a, b))
{
    writeln(e1); // a[idx]
    writeln(e2); // b[idx]
    ++idx;
}
Примеры:
zip — мощный инструмент; следующий код сортирует два массива параллельно:
import std.algorithm.sorting : sort;

int[] a = [ 1, 2, 3 ];
string[] b = [ "a", "c", "b" ];
zip(a, b).sort!((t1, t2) => t1[0] > t2[0]);

writeln(a); // [3, 2, 1]
// b is sorted according to a's sorting
writeln(b); // ["b", "c", "a"]
this(R rs, StoppingPolicy s = StoppingPolicy.shortest);

Создаёт объект. Обычно вызывается косвенно с помощью функции zip.

enum bool empty;

Возвращает true, если диапазон достиг конца. Проверка зависит от политики остановки.

@property Zip save();
@property ElementType front();

Возвращает текущий итерируемый элемент.

@property void front(ElementType v);

Устанавливает начало всех итерируемых диапазонов.

ElementType moveFront();

Выталкивает начало.

@property ElementType back();

Возвращает правый крайний элемент.

ElementType moveBack();

Выталкивает конец.

Возвращает правый крайний элемент.

@property void back(ElementType v);

Возвращает текущий итерируемый элемент.

Возвращает правый крайний элемент.

void popFront();

Перемещается к следующему элементу во всех управляемых диапазонах.

void popBack();

Вызывает popBack для всех управляемых диапазонов.

@property auto length();

Возвращает длину этого диапазона. Определено только если все диапазоны определяют length.

alias opDollar = length;

Возвращает длину этого диапазона. Определено только если все диапазоны определяют length.

auto opSlice(size_t from, size_t to);

Возвращает срез диапазона. Определяется только если все диапазоны поддерживают срез.

ElementType opIndex(size_t n);

Возвращает n-ый элемент в составном диапазоне. Определено, если все диапазоны поддерживают произвольный доступ.

void opIndexAssign(ElementType v, size_t n);

Присваивает значение n-ому элементу в составном диапазоне. Определено, если все диапазоны поддерживают произвольный доступ.

Возвращает n-ый элемент в составном диапазоне. Определено, если все диапазоны поддерживают произвольный доступ.

ElementType moveAt(size_t n);

Деструктивно считывает n-ый элемент в составном диапазоне. Определено, если все диапазоны поддерживают произвольный доступ.

Возвращает n-ый элемент в составном диапазоне. Определено, если все диапазоны поддерживают произвольный доступ.

enum StoppingPolicy: int;

Указывает, как должна прекращаться итерация в zip и lockstep. По умолчанию, остановка происходит по достижению конца самого короткого диапазона.

Примеры:
import std.algorithm.comparison : equal;
import std.exception : assertThrown;
import std.range.primitives;
import std.typecons : tuple;

auto a = [1, 2, 3];
auto b = [4, 5, 6, 7];

auto shortest = zip(StoppingPolicy.shortest, a, b);
assert(shortest.equal([
    tuple(1, 4),
    tuple(2, 5),
    tuple(3, 6)
]));

auto longest = zip(StoppingPolicy.longest, a, b);
assert(longest.equal([
    tuple(1, 4),
    tuple(2, 5),
    tuple(3, 6),
    tuple(0, 7)
]));

auto same = zip(StoppingPolicy.requireSameLength, a, b);
same.popFrontN(3);
assertThrown!Exception(same.popFront);
shortest

Остановка по достижению конца самого короткого диапазона

longest

Остановка по достижению конца самого длинного диапазона

requireSameLength

Требование, чтобы все диапазоны имели одинаковую длину

struct Lockstep(Ranges...) if (Ranges.length > 1 && allSatisfy!(isInputRange, Ranges));

Lockstep!Ranges lockstep(Ranges...)(Ranges ranges)
Constraints: if (allSatisfy!(isInputRange, Ranges));

Lockstep!Ranges lockstep(Ranges...)(Ranges ranges, StoppingPolicy s)
Constraints: if (allSatisfy!(isInputRange, Ranges));

Проход по нескольким диапазонам синхронно с помощью цикла foreach. В отличие от zip, это позволяет получать доступ к элементам по ссылке. Если передан только один диапазон, Lockstep меняет своё поведение. Если диапазоны имеют разную длину и s == StoppingPolicy.shortest, цикл останавливается после того, как самый короткий диапазон станет пустым. Если диапазоны имеют разную длину и s == StoppingPolicy.requireSameLength, генерируется исключение. s не может быть StoppingPolicy.longest, и передача этого значения вызовет исключение.

Итерация по Lockstep в обратном порядке и с индексом возможна только когда s == StoppingPolicy.requireSameLength, чтобы сохранить индексы. Если попытка итерации в обратном порядке, когда s == StoppingPolicy.shortest, будет предпринята, будет выброшено исключение.

По умолчанию StoppingPolicy установлено в StoppingPolicy.shortest.

Ограничения
Атрибуты pure, @safe, @nogc, или nothrow не могут быть выведены для итерации lockstep. zip может вывести первые два из-за другой реализации.
См. также:
zip lockstep аналогичен zip, но zip объединяет свои элементы и возвращает диапазон. lockstep также поддерживает доступ по ссылке. Используйте zip если вы хотите передать результат функции диапазона.
Примеры:
auto arr1 = [1,2,3,4,5,100];
auto arr2 = [6,7,8,9,10];

foreach (ref a, b; lockstep(arr1, arr2))
{
    a += b;
}

writeln(arr1); // [7, 9, 11, 13, 15, 100]

/// Lockstep also supports iterating with an index variable:
foreach (index, a, b; lockstep(arr1, arr2))
{
    writeln(arr1[index]); // a
    writeln(arr2[index]); // b
}
this(R ranges, StoppingPolicy sp = StoppingPolicy.shortest);
struct Recurrence(alias fun, StateType, size_t stateSize);

Recurrence!(fun, CommonType!State, State.length) recurrence(alias fun, State...)(State initial);

Создаёт математическую последовательность, заданную начальными значениями и рекуррентной функцией, вычисляющей следующее значение по имеющимся. Последовательность представлена бесконечным направленным диапазоном. Тип Recurrence сам по себе редко используется напрямую; чаще всего рекуррентные последовательности получаются вызовом функции recurrence.

При вызове recurrence, функция, вычисляющая следующее значение, указывается как шаблонный аргумент, а начальные значения рекуррентной последовательности передаются как обычные аргументы. Например, в последовательности Фибоначчи есть два начальных значения (и, следовательно, размер состояния 2), потому что для вычисления следующего значения Фибоначчи необходимы два предыдущих значения.

Подпись этой функции должна быть:

auto fun(R)(R state, size_t n)
где n будет индексом текущего значения, а state — непрозрачный вектор состояния, который может быть индексирован с помощью обозначения индекса массива state[i], где допустимые значения i находятся в диапазоне от (n - 1) до (n - State.length).

Если функция передаётся в строковой форме, состояние имеет имя "a", а индекс с нуля в рекуррентной последовательности имеет имя "n". Переданная строка должна возвращать желаемое значение для a[n] при заданных a[n - 1], a[n - 2], a[n - 3],..., a[n - stateSize]. Размер состояния определяется количеством аргументов, переданных в вызов recurrence. Структура Recurrence сама заботится о управлении состоянием рекуррентной последовательности и его соответствующем смещении.

Примеры:
import std.algorithm.comparison : equal;

// The Fibonacci numbers, using function in string form:
// a[0] = 1, a[1] = 1, and compute a[n+1] = a[n-1] + a[n]
auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1);
assert(fib.take(10).equal([1, 1, 2, 3, 5, 8, 13, 21, 34, 55]));

// The factorials, using function in lambda form:
auto fac = recurrence!((a,n) => a[n-1] * n)(1);
assert(take(fac, 10).equal([
    1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880
]));

// The triangular numbers, using function in explicit form:
static size_t genTriangular(R)(R state, size_t n)
{
    return state[n-1] + n;
}
auto tri = recurrence!genTriangular(0);
assert(take(tri, 10).equal([0, 1, 3, 6, 10, 15, 21, 28, 36, 45]));
struct Sequence(alias fun, State);

auto sequence(alias fun, State...)(State args);

Sequence похожа на Recurrence, за исключением того, что итерация представлена в так называемой закрытой форме. Это означает, что n-ый элемент в ряду вычисляется непосредственно из начальных значений и самого n. Это подразумевает, что интерфейс, предоставляемый Sequence, является диапазоном с произвольным доступом, в отличие от обычного Recurrence, который предоставляет только итерацию вперёд.

Состояние последовательности хранится как Tuple, поэтому оно может быть неоднородным.

Примеры:
Нечётные числа, используя функцию в строковом формате:
auto odds = sequence!("a[0] + n * a[1]")(1, 2);
writeln(odds.front); // 1
odds.popFront();
writeln(odds.front); // 3
odds.popFront();
writeln(odds.front); // 5
Примеры:
Треугольные числа, используя функцию в лямбда-форме:
auto tri = sequence!((a,n) => n*(n+1)/2)();

// Note random access
writeln(tri[0]); // 0
writeln(tri[3]); // 6
writeln(tri[1]); // 1
writeln(tri[4]); // 10
writeln(tri[2]); // 3
Примеры:
Числа Фибоначчи, используя функцию в явном виде:
import std.math : pow, round, sqrt;
static ulong computeFib(S)(S state, size_t n)
{
    // Binet's formula
    return cast(ulong)(round((pow(state[0], n+1) - pow(state[1], n+1)) /
                             state[2]));
}
auto fib = sequence!computeFib(
    (1.0 + sqrt(5.0)) / 2.0,    // Golden Ratio
    (1.0 - sqrt(5.0)) / 2.0,    // Conjugate of Golden Ratio
    sqrt(5.0));

// Note random access with [] operator
writeln(fib[1]); // 1
writeln(fib[4]); // 5
writeln(fib[3]); // 3
writeln(fib[2]); // 2
writeln(fib[9]); // 55
auto iota(B, E, S)(B begin, E end, S step)
Constraints: if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) && isIntegral!S);

auto iota(B, E)(B begin, E end)
Constraints: if (isFloatingPoint!(CommonType!(B, E)));

auto iota(B, E)(B begin, E end)
Constraints: if (isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E)));

auto iota(E)(E end)
Constraints: if (is(typeof(iota(E(0), end))));

auto iota(B, E, S)(B begin, E end, S step)
Constraints: if (isFloatingPoint!(CommonType!(B, E, S)));

auto iota(B, E)(B begin, E end)
Constraints: if (!isIntegral!(CommonType!(B, E)) && !isFloatingPoint!(CommonType!(B, E)) && !isPointer!(CommonType!(B, E)) && is(typeof((ref B b) { ++b; } )) && (is(typeof(B.init < E.init)) || is(typeof(B.init == E.init))));

Создаёт диапазон значений, охватывающий заданные начальное и конечное значения.

Параметры:
B begin Начальное значение.
E end Значение, которое служит критерием остановки. Это значение не включается в диапазон.
S step Значение, добавляемое к текущему значению на каждой итерации.
Возвращает:
Диапазон, проходящий по числам begin, begin + step, begin + 2 * step, ..., до и не включая end. Перегрузки с двумя аргументами имеют step = 1. Если begin < end && step < 0 или begin > end && step > 0 или begin == end, то возвращается пустой диапазон. Если step == 0, то begin == end — ошибка. Для встроенных типов возвращаемый диапазон — диапазон с произвольным доступом. Для пользовательских типов, поддерживающих ++, диапазон является диапазоном ввода. Целочисленный iota также поддерживает оператор in справа. Он учитывает шаг, целочисленный тип не будет считаться содержащимся, если он попадает между двумя последовательными значениями диапазона. contains делает то же самое, но слева.
Пример
void main()
{
    import std.stdio;

    // The following groups all produce the same output of:
    // 0 1 2 3 4

    foreach (i; 0 .. 5)
        writef("%s ", i);
    writeln();

    import std.range : iota;
    foreach (i; iota(0, 5))
        writef("%s ", i);
    writeln();

    writefln("%(%s %|%)", iota(0, 5));

    import std.algorithm.iteration : map;
    import std.algorithm.mutation : copy;
    import std.format;
    iota(0, 5).map!(i => format("%s ", i)).copy(stdout.lockingTextWriter());
    writeln();
}
Примеры:
import std.algorithm.comparison : equal;
import std.math : approxEqual;

auto r = iota(0, 10, 1);
assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
assert(3 in r);
assert(r.contains(3)); //Same as above
assert(!(10 in r));
assert(!(-8 in r));
r = iota(0, 11, 3);
assert(equal(r, [0, 3, 6, 9]));
writeln(r[2]); // 6
assert(!(2 in r));
auto rf = iota(0.0, 0.5, 0.1);
assert(approxEqual(rf, [0.0, 0.1, 0.2, 0.3, 0.4]));
enum TransverseOptions: int;

Параметры для диапазонов FrontTransversal и Transversal (ниже).

Примеры:
import std.algorithm.comparison : equal;
import std.exception : assertThrown;

auto arr = [[1, 2], [3, 4, 5]];

auto r1 = arr.frontTransversal!(TransverseOptions.assumeJagged);
assert(r1.equal([1, 3]));

// throws on construction
assertThrown!Exception(arr.frontTransversal!(TransverseOptions.enforceNotJagged));

auto r2 = arr.frontTransversal!(TransverseOptions.assumeNotJagged);
assert(r2.equal([1, 3]));

// either assuming or checking for equal lengths makes
// the result a random access range
writeln(r2[0]); // 1
static assert(!__traits(compiles, r1[0]));
assumeJagged

При обходе элементы диапазона диапазонов предполагаются с разной длиной (например, неровный массив).

enforceNotJagged

Обход требует, чтобы элементы диапазона диапазонов имели одинаковую длину (например, массив массивов, все с одинаковой длиной). Проверка выполняется один раз при построении диапазона обхода.

assumeNotJagged

Обход предполагает (без проверки), что элементы диапазона диапазонов имеют одинаковую длину. Этот параметр полезен, если проверка уже выполнена снаружи диапазона.

struct FrontTransversal(Ror, TransverseOptions opt = TransverseOptions.assumeJagged);

FrontTransversal!(RangeOfRanges, opt) frontTransversal(TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges)(RangeOfRanges rr);

Дано диапазон диапазонов, поперечный обход через первые элементы каждого из вложенных диапазонов.

Примеры:
import std.algorithm.comparison : equal;
int[][] x = new int[][2];
x[0] = [1, 2];
x[1] = [3, 4];
auto ror = frontTransversal(x);
assert(equal(ror, [ 1, 3 ][]));
this(RangeOfRanges input);

Конструирование из входных данных.

enum bool empty;

@property ref auto front();

ElementType moveFront();

void popFront();

Примитивы направленного диапазона.

@property FrontTransversal save();

Дублирует этот frontTransversal. Обратите внимание, что дублируется только охватывающий диапазон диапазонов. Вложенные диапазоны не дублируются.

@property ref auto back();

void popBack();

ElementType moveBack();

Бинарные примитивы. Они предоставляются, если isBidirectionalRange!RangeOfRanges.

ref auto opIndex(size_t n);

ElementType moveAt(size_t n);

void opIndexAssign(ElementType val, size_t n);

Примитив произвольного доступа. Он предоставляется, если isRandomAccessRange!RangeOfRanges && (opt == TransverseOptions.assumeNotJagged || opt == TransverseOptions.enforceNotJagged).

typeof(this) opSlice(size_t lower, size_t upper);

Вырезка, если предоставляется, если RangeOfRanges поддерживает вырезку и все условия для поддержки индексации выполнены.

struct Transversal(Ror, TransverseOptions opt = TransverseOptions.assumeJagged);

Transversal!(RangeOfRanges, opt) transversal(TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges)(RangeOfRanges rr, size_t n);

При заданном диапазоне диапазонов, итерируйте поперечно через n-й элемент каждого из вложенных диапазонов. Эта функция похожа на unzip в других языках.

Параметры:
opt Управляет предположениями, которые функция делает о длине диапазонов
RangeOfRanges rr Входной диапазон диапазонов с произвольным доступом
Возвращает:
Как минимум, входной диапазон. Примитивы диапазонов, такие как двунаправленность и произвольный доступ, предоставляются, если тип элемента rr их предоставляет.
Примеры:
import std.algorithm.comparison : equal;
int[][] x = new int[][2];
x[0] = [1, 2];
x[1] = [3, 4];
auto ror = transversal(x, 1);
assert(equal(ror, [ 2, 4 ]));
Примеры:
Следующий код выполняет полное распаковку
import std.algorithm.comparison : equal;
import std.algorithm.iteration : map;
int[][] y = [[1, 2, 3], [4, 5, 6]];
auto z = y.front.walkLength.iota.map!(i => transversal(y, i));
assert(equal!equal(z, [[1, 4], [2, 5], [3, 6]]));
this(RangeOfRanges input, size_t n);

Конструктор из входных данных и индекса.

enum bool empty;

@property ref auto front();

E moveFront();

@property void front(E val);

void popFront();

@property typeof(this) save();

Примитивы диапазонов вперёд.

@property ref auto back();

void popBack();

E moveBack();

@property void back(E val);

Двунаправленные примитивы. Они предлагаются, если isBidirectionalRange!RangeOfRanges.

ref auto opIndex(size_t n);

E moveAt(size_t n);

void opIndexAssign(E val, size_t n);

Примитив произвольного доступа. Он предлагается, если isRandomAccessRange!RangeOfRanges && (opt == TransverseOptions.assumeNotJagged || opt == TransverseOptions.enforceNotJagged).

typeof(this) opSlice(size_t lower, size_t upper);

Операция слайсинга, если RangeOfRanges поддерживает слайсинг и выполнены все условия для поддержки индексирования.

Transposed!(RangeOfRanges, opt) transposed(TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges)(RangeOfRanges rr)
Constraints: if (isForwardRange!RangeOfRanges && isInputRange!(ElementType!RangeOfRanges) && hasAssignableElements!RangeOfRanges);

При заданном диапазоне диапазонов возвращает диапазон диапазонов, где i-й поддиапазон содержит i-й элемент исходных поддиапазонов.

Transposed в настоящее время определяет save, но не работает как диапазон вперёд. Потребление копии, созданной с помощью save , потребляет все копии, даже исходные поддиапазоны, поданные в Transposed.

Параметры:
opt Управляет предположениями, которые функция делает о длине диапазонов (например, зубчатые или нет)
RangeOfRanges rr Диапазон диапазонов
Примеры:
import std.algorithm.comparison : equal;
int[][] ror = [
    [1, 2, 3],
    [4, 5, 6]
];
auto xp = transposed(ror);
assert(equal!"a.equal(b)"(xp, [
    [1, 4],
    [2, 5],
    [3, 6]
]));
Примеры:
int[][] x = new int[][2];
x[0] = [1, 2];
x[1] = [3, 4];
auto tr = transposed(x);
int[][] witness = [ [ 1, 3 ], [ 2, 4 ] ];
uint i;

foreach (e; tr)
{
    writeln(array(e)); // witness[i++]
}
struct Indexed(Source, Indices) if (isRandomAccessRange!Source && isInputRange!Indices && is(typeof(Source.init[ElementType!Indices.init])));

Indexed!(Source, Indices) indexed(Source, Indices)(Source source, Indices indices);

Этот struct принимает два диапазона, source и indices, и создаёт представление source как если бы его элементы были переупорядочены в соответствии с indices. indices может включать только подмножество элементов source и может также повторять элементы.

Source должен быть диапазоном с произвольным доступом. Возвращаемый диапазон будет двунаправленным или с произвольным доступом, если Indices двунаправленный или с произвольным доступом соответственно.

Примеры:
import std.algorithm.comparison : equal;
auto source = [1, 2, 3, 4, 5];
auto indices = [4, 3, 1, 2, 0, 4];
auto ind = indexed(source, indices);
assert(equal(ind, [5, 4, 2, 3, 1, 5]));
assert(equal(retro(ind), [5, 1, 3, 2, 4, 5]));
@property ref auto front();

void popFront();

@property typeof(this) save();

@property ref auto front(ElementType!Source newVal);

auto moveFront();

@property ref auto back();

void popBack();

@property ref auto back(ElementType!Source newVal);

auto moveBack();

ref auto opIndex(size_t index);

typeof(this) opSlice(size_t a, size_t b);

auto opIndexAssign(ElementType!Source newVal, size_t index);

auto moveAt(size_t index);

Примитивы диапазонов

@property Source source();

Возвращает исходный диапазон.

@property Indices indices();

Возвращает диапазон индексов.

size_t physicalIndex(size_t logicalIndex);

Возвращает физический индекс в исходном диапазоне, соответствующий заданному логическому индексу. Это полезно, например, при индексировании Indexed без добавления другого уровня косвенности.

Примеры:
auto ind = indexed([1, 2, 3, 4, 5], [1, 3, 4]);
writeln(ind.physicalIndex(0)); // 1
struct Chunks(Source) if (isInputRange!Source);

Chunks!Source chunks(Source)(Source source, size_t chunkSize)
Constraints: if (isInputRange!Source);

Этот диапазон итерируется по частям фиксированного размера chunkSize диапазона source. Source должен быть диапазоном ввода. chunkSize должно быть больше нуля.

Если !isInfinite!Source и source.walkLength не делится нацело на chunkSize, последний элемент этого диапазона будет содержать меньше чем chunkSize элементов.

Если Source является диапазоном вперёд, результирующий диапазон также будет диапазонами вперёд. В противном случае, результирующие части будут диапазонами ввода, потребляющими тот же ввод: итерирование по front уменьшит часть таким образом, что последующие вызовы front больше не будут возвращать полную часть, и вызов popFront на внешнем диапазоне сделает недействительными любые оставшиеся ссылки на предыдущие значения front.

Параметры:
Source source Диапазон, из которого будут выбраны части
size_t chunkSize Размер части
См. также:
slide
Возвращает:
Диапазон частей.
Примеры:
import std.algorithm.comparison : equal;
auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
auto chunks = chunks(source, 4);
writeln(chunks[0]); // [1, 2, 3, 4]
writeln(chunks[1]); // [5, 6, 7, 8]
writeln(chunks[2]); // [9, 10]
writeln(chunks.back); // chunks[2]
writeln(chunks.front); // chunks[0]
writeln(chunks.length); // 3
assert(equal(retro(array(chunks)), array(retro(chunks))));
Примеры:
Поддерживаются диапазоны ввода, не являющиеся диапазонами вперёд, но с ограниченной семантикой.
import std.algorithm.comparison : equal;

int i;

// The generator doesn't save state, so it cannot be a forward range.
auto inputRange = generate!(() => ++i).take(10);

// We can still process it in chunks, but it will be single-pass only.
auto chunked = inputRange.chunks(2);

assert(chunked.front.equal([1, 2]));
assert(chunked.front.empty); // Iterating the chunk has consumed it
chunked.popFront;
assert(chunked.front.equal([3, 4]));
this(Source source, size_t chunkSize);

Стандартный конструктор

@property auto front();

void popFront();

@property bool empty();

Примитивы диапазона ввода. Всегда присутствуют.

@property typeof(this) save();

Примитивы диапазона вперёд. Присутствуют только если Source является диапазоном вперёд.

@property size_t length();

Длина. Только если hasLength!Source является true

auto opIndex(size_t index);

typeof(this) opSlice(size_t lower, size_t upper);

Операции индексирования и слайсинга. Предоставляются только если hasSlicing!Source является true.

@property auto back();

void popBack();

Двунаправленные примитивы диапазонов. Предоставляются только если как hasSlicing!Source , так и hasLength!Source являются true.

struct EvenChunks(Source) if (isForwardRange!Source && hasLength!Source);

EvenChunks!Source evenChunks(Source)(Source source, size_t chunkCount)
Constraints: if (isForwardRange!Source && hasLength!Source);

Этот диапазон разбивает диапазон source на chunkCount части приблизительно равной длины. Source должен быть диапазоном вперёд с известной длиной.

В отличие от chunks, evenChunks принимает количество частей (а не размер). Возвращаемый диапазон будет содержать ноль или более элементов source.length / chunkCount + 1 , после чего следует source.length / chunkCount элементов. Если source.length < chunkCount, некоторые части будут пустыми.

chunkCount не должен быть нулём, если source также пуст.

Примеры:
import std.algorithm.comparison : equal;
auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
auto chunks = evenChunks(source, 3);
writeln(chunks[0]); // [1, 2, 3, 4]
writeln(chunks[1]); // [5, 6, 7]
writeln(chunks[2]); // [8, 9, 10]
this(Source source, size_t chunkCount);

Стандартный конструктор

@property auto front();

void popFront();

@property bool empty();

@property typeof(this) save();

Примитивы диапазона вперёд. Всегда присутствуют.

const @property size_t length();

Длина

auto opIndex(size_t index);

typeof(this) opSlice(size_t lower, size_t upper);

@property auto back();

void popBack();

Операции индексирования, слайсинга и двунаправленного доступа и примитивы диапазонов. Предоставляются только если hasSlicing!Source является true.

auto slide(Flag!"withPartial" f = Yes.withPartial, Source)(Source source, size_t windowSize, size_t stepSize = 1)
Constraints: if (isForwardRange!Source);

Итерация с фиксированным размером скользящего окна размером windowSize по диапазону source с помощью пользовательского stepSize.

Диапазон Source должен быть как минимум ForwardRange, а windowSize должно быть больше нуля.

Для windowSize = 1 он разбивает диапазон на группы по одному элементу (также известные как unflatten) Для windowSize = 2 он аналогичен zip(source, source.save.dropOne).

Параметры:
f Указывает, следует ли игнорировать последний элемент, если в нём меньше элементов, чем windowSize (No.withPartial) или добавить его (Yes.withPartial)
Source source Диапазон, из которого будет выбран слайс
size_t windowSize Размер скользящего окна
size_t stepSize Шаг между окнами (по умолчанию 1)
Возвращает:
Диапазон всех скользящих окон с проинициализированной двунаправленностью, вперёд, произвольным доступом и слайсингом.
Примечание
Для избежания накладных расходов на производительность, двунаправленность доступна только тогда, когда std.range.primitives.hasSlicing и std.range.primitives.hasLength являются истинными.
См. также:
chunks
Примеры:
Итерирование по диапазонам с окнами
import std.algorithm.comparison : equal;

assert([0, 1, 2, 3].slide(2).equal!equal(
    [[0, 1], [1, 2], [2, 3]]
));

assert(5.iota.slide(3).equal!equal(
    [[0, 1, 2], [1, 2, 3], [2, 3, 4]]
));
Примеры:
Установите пользовательский шаг (по умолчанию 1)
import std.algorithm.comparison : equal;

assert(6.iota.slide(1, 2).equal!equal(
    [[0], [2], [4]]
));

assert(6.iota.slide(2, 4).equal!equal(
    [[0, 1], [4, 5]]
));

assert(iota(7).slide(2, 2).equal!equal(
    [[0, 1], [2, 3], [4, 5], [6]]
));

assert(iota(12).slide(2, 4).equal!equal(
    [[0, 1], [4, 5], [8, 9]]
));
Примеры:
Разрешить последнему слайсу иметь меньше элементов, чем windowSize
import std.algorithm.comparison : equal;

assert(3.iota.slide!(No.withPartial)(4).empty);
assert(3.iota.slide!(Yes.withPartial)(4).equal!equal(
    [[0, 1, 2]]
));
Примеры:
Подсчитать все возможные подстроки длиной 2
import std.algorithm.iteration : each;

int[dstring] d;
"AGAGA"d.slide!(Yes.withPartial)(2).each!(a => d[a]++);
writeln(d); // ["AG"d:2, "GA"d:2]
Примеры:
withPartial имеет эффект только в том случае, если последний элемент в диапазоне не имеет полного размера
import std.algorithm.comparison : equal;

assert(5.iota.slide!(Yes.withPartial)(3, 4).equal!equal([[0, 1, 2], [4]]));
assert(6.iota.slide!(Yes.withPartial)(3, 4).equal!equal([[0, 1, 2], [4, 5]]));
assert(7.iota.slide!(Yes.withPartial)(3, 4).equal!equal([[0, 1, 2], [4, 5, 6]]));

assert(5.iota.slide!(No.withPartial)(3, 4).equal!equal([[0, 1, 2]]));
assert(6.iota.slide!(No.withPartial)(3, 4).equal!equal([[0, 1, 2]]));
assert(7.iota.slide!(No.withPartial)(3, 4).equal!equal([[0, 1, 2], [4, 5, 6]]));
auto only(Values...)(return scope Values values)
Constraints: if (!is(CommonType!Values == void) || Values.length == 0);

Сборка values в диапазон, содержащий все его элементы in-situ.

Полезно, когда одно значение или несколько разрозненных значений должны быть переданы алгоритму, ожидающему диапазон, без динамического выделения памяти.

Поскольку копирование диапазона означает копирование всех элементов, его можно безопасно возвращать из функций. По той же причине копирование возвращенного диапазона может быть дорогостоящим для большого количества аргументов.

Параметры:
Values values значения для сборки вместе
Возвращает:
Диапазон собранных значений.
См. также:
chain для объединения диапазонов
Примеры:
import std.algorithm.comparison : equal;
import std.algorithm.iteration : filter, joiner, map;
import std.algorithm.searching : findSplitBefore;
import std.uni : isUpper;

assert(equal(only('♡'), "♡"));
writeln([1, 2, 3, 4].findSplitBefore(only(3))[0]); // [1, 2]

assert(only("one", "two", "three").joiner(" ").equal("one two three"));

string title = "The D Programming Language";
assert(title
    .filter!isUpper // take the upper case letters
    .map!only       // make each letter its own range
    .joiner(".")    // join the ranges together lazily
    .equal("T.D.P.L"));
auto enumerate(Enumerator = size_t, Range)(Range range, Enumerator start = 0)
Constraints: if (isIntegral!Enumerator && isInputRange!Range);

Итерация по range с прикрепленной переменной индекса.

Каждый элемент является std.typecons.Tuple, содержащим индекс и элемент в этом порядке, где член индекса имеет имя index, а член элемента имеет имя value.

Индекс начинается с start и увеличивается на единицу на каждой итерации.

Переполнение
Если range имеет длину, то передача значения для start таким образом, что start + range.length больше Enumerator.max, является ошибкой, что гарантирует невозможность переполнения.
Если range не имеет длины, и popFront вызывается, когда front.index == Enumerator.max, индекс переполнится и продолжится с Enumerator.min.
Параметры:
Range range входной диапазон для добавления индексов
Enumerator start число, с которого начать счетчик индексов
Возвращает:
По меньшей мере, входной диапазон. Все остальные диапазонные примитивы предоставляются в результирующем диапазоне, если у range есть они. Исключения составляют двунаправленные примитивы, которые передаются только в том случае, если у range есть длина.
Пример
Полезно для использования foreach с циклом по индексу:
    import std.stdio : stdin, stdout;
    import std.range : enumerate;

    foreach (lineNum, line; stdin.byLine().enumerate(1))
        stdout.writefln("line #%s: %s", lineNum, line);
Примеры:
Можно начать перечисление с отрицательной позиции:
import std.array : assocArray;
import std.range : enumerate;

bool[int] aa = true.repeat(3).enumerate(-1).assocArray();
assert(aa[-1]);
assert(aa[0]);
assert(aa[1]);
enum auto isTwoWayCompatible(alias fn, T1, T2);

Возвращает true, если fn принимает переменные типа T1 и T2 в любом порядке. Следующий код должен компилироваться:

(ref T1 a, ref T2 b)
{
  fn(a, b);
  fn(b, a);
}

Примеры:
void func1(int a, int b);
void func2(int a, float b);

static assert(isTwoWayCompatible!(func1, int, int));
static assert(isTwoWayCompatible!(func1, short, int));
static assert(!isTwoWayCompatible!(func2, int, float));

void func3(ref int a, ref int b);
static assert( isTwoWayCompatible!(func3, int, int));
static assert(!isTwoWayCompatible!(func3, short, int));
enum SearchPolicy: int;

Политика, используемая с примитивами поиска lowerBound, upperBound, и equalRange из SortedRange ниже.

Примеры:
import std.algorithm.comparison : equal;

auto a = assumeSorted([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
auto p1 = a.upperBound!(SearchPolicy.binarySearch)(3);
assert(p1.equal([4, 5, 6, 7, 8, 9]));

auto p2 = a.lowerBound!(SearchPolicy.gallop)(4);
assert(p2.equal([0, 1, 2, 3]));
linear

Поиск линейным способом.

trot

Поиск с шагом, который линейно возрастает (1, 2, 3,...), что приводит к квадратичному графику поиска (используемые индексы 0, 1, 3, 6, 10, 15, 21, 28,...). После того, как поиск переходит через целевое значение, оставшийся интервал ищется с помощью бинарного поиска. Поиск завершается за время Ο(sqrt(n)). Используйте его, когда вы с достаточной уверенностью знаете, что значение находится примерно в начале диапазона.

gallop

Выполняет алгоритм поиска галопом, т.е. поиск с шагом, который удваивается каждый раз (1, 2, 4, 8,...), что приводит к экспоненциальному графику поиска (используемые индексы 0, 1, 3, 7, 15, 31, 63,...). После того, как поиск переходит через целевое значение, оставшийся интервал ищется с помощью бинарного поиска. Значение находится за время Ο(log(n)).

binarySearch

Поиск с помощью классической политики деления интервала пополам. Поиск начинается посередине диапазона, и на каждом шаге поиска диапазон делится пополам. Эта политика находит значение за время Ο(log(n)), но менее дружелюбна к кешу, чем gallop для больших диапазонов. Политика binarySearch используется в качестве последнего шага для trot, gallop, trotBackwards, и gallopBackwards стратегий.

trotBackwards

Аналогично trot , но начинается в обратном направлении. Используйте его, когда уверены, что значение находится примерно в конце диапазона.

gallopBackwards

Аналогично gallop , но начинается в обратном направлении. Используйте его, когда уверены, что значение находится примерно в конце диапазона.

enum SortedRangeOptions: int;

Параметры для SortedRange диапазонов (ниже).

Примеры:
// create a SortedRange, that's checked strictly
SortedRange!(int[],"a < b", SortedRangeOptions.checkStrictly)([ 1, 3, 5, 7, 9 ]);
assumeSorted

Предполагается, что диапазон отсортирован без проверки.

checkStrictly

Все элементы диапазона проверяются на отсортированность. Проверка выполняется за время O(n).

checkRoughly

Некоторые элементы диапазона проверяются на отсортированность. Для диапазонов с произвольным порядком это, почти наверняка, обнаружит, что он не отсортирован. Для почти отсортированных диапазонов вероятность неудачи выше. Проверяемые элементы выбираются детерминированным образом, что делает эту проверку воспроизводимой. Проверка выполняется за время O(log(n)).

struct SortedRange(Range, alias pred = "a < b", SortedRangeOptions opt = SortedRangeOptions.assumeSorted) if (isInputRange!Range && !isInstanceOf!(SortedRange, Range));

template SortedRange(Range, alias pred = "a < b", SortedRangeOptions opt = SortedRangeOptions.assumeSorted) if (isInstanceOf!(SortedRange, Range))

Представляет отсортированный диапазон. Помимо стандартных операций с диапазонами, поддерживает дополнительные операции, использующие сортировку, такие как слияние и бинарный поиск. Для получения SortedRange из неотсортированного диапазона r, используйте std.algorithm.sorting.sort, который сортирует r на месте и возвращает соответствующий SortedRange. Для построения SortedRange из диапазона r, который известен как уже отсортированный, используйте assumeSorted.

Параметры:
Примеры:
import std.algorithm.sorting : sort;
auto a = [ 1, 2, 3, 42, 52, 64 ];
auto r = assumeSorted(a);
assert(r.contains(3));
assert(!(32 in r));
auto r1 = sort!"a > b"(a);
assert(3 in r1);
assert(!r1.contains(32));
writeln(r1.release()); // [64, 52, 42, 3, 2, 1]
Примеры:
SortedRange может принимать диапазоны, слабее случайного доступа, но не может предоставить интересные функции для них. Поэтому SortedRange в настоящее время ограничен диапазонами случайного доступа. Копия исходного диапазона никогда не создается. Если базовый диапазон изменяется одновременно с соответствующим SortedRange таким образом, что нарушается сортировка, SortedRange будет работать нестабильно.
import std.algorithm.mutation : swap;
auto a = [ 1, 2, 3, 42, 52, 64 ];
auto r = assumeSorted(a);
assert(r.contains(42));
swap(a[3], a[5]);         // illegal to break sortedness of original range
assert(!r.contains(42));  // passes although it shouldn't
@property bool empty();

@property auto save();

@property ref auto front();

void popFront();

@property ref auto back();

void popBack();

ref auto opIndex(size_t i);

scope auto opSlice(size_t a, size_t b) return;

Базовые операции с диапазонами.

auto release();

Освобождает управляемый диапазон и возвращает его.

auto lowerBound(SearchPolicy sp = SearchPolicy.binarySearch, V)(V value)
Constraints: if (isTwoWayCompatible!(predFun, ElementType!Range, V) && hasSlicing!Range);

Эта функция использует поиск с политикой sp для нахождения наибольшего левого поддиапазона, на котором pred(x, value) является true для всех x (например, если pred — "меньше", возвращает часть диапазона с элементами, строго меньшими value). Расписание поиска и его сложность описаны в SearchPolicy.

Примеры:
import std.algorithm.comparison : equal;
auto a = assumeSorted([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]);
auto p = a.lowerBound(4);
assert(equal(p, [ 0, 1, 2, 3 ]));
auto upperBound(SearchPolicy sp = SearchPolicy.binarySearch, V)(V value)
Constraints: if (isTwoWayCompatible!(predFun, ElementType!Range, V));

Эта функция ищет с политикой sp для нахождения наибольшего правого поддиапазона, на котором pred(value, x) является true для всех x (например, если pred — "меньше", возвращает часть диапазона с элементами, строго большими value). Расписание поиска и его сложность описаны в SearchPolicy.

Для диапазонов, которые не поддерживают случайный доступ, разрешена только политика SearchPolicy.linear (и она должна быть указана явно, чтобы не подвергать код пользователя неожиданным неэффективностям). Для поисков с случайным доступом разрешены все политики, и SearchPolicy.binarySearch является по умолчанию.

Примеры:
import std.algorithm.comparison : equal;
auto a = assumeSorted([ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]);
auto p = a.upperBound(3);
assert(equal(p, [4, 4, 5, 6]));
auto equalRange(V)(V value)
Constraints: if (isTwoWayCompatible!(predFun, ElementType!Range, V) && isRandomAccessRange!Range);

Возвращает поддиапазон, содержащий все элементы e, для которых как pred(e, value), так и pred(value, e) возвращают false (например, если pred — "меньше", возвращает часть диапазона с элементами, равными value). Использует классический бинарный поиск с делением интервала пополам, пока не найдёт значение, удовлетворяющее условию, затем использует SearchPolicy.gallopBackwards для нахождения левой границы и SearchPolicy.gallop для нахождения правой границы. Эти политики оправданы тем, что обе границы, скорее всего, будут находиться рядом с первым найденным значением (т. е. диапазоны с равными элементами относительно малы). Завершает весь поиск за Ο(log(n)) времени.

Примеры:
import std.algorithm.comparison : equal;
auto a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ];
auto r = a.assumeSorted.equalRange(3);
assert(equal(r, [ 3, 3, 3 ]));
auto trisect(V)(V value)
Constraints: if (isTwoWayCompatible!(predFun, ElementType!Range, V) && isRandomAccessRange!Range && hasLength!Range);

Возвращает кортеж r, такой что r[0] совпадает с результатом lowerBound(value), r[1] совпадает с результатом equalRange(value), и r[2] совпадает с результатом upperBound(value). Вызов быстрее, чем вычисление всех трёх по отдельности. Использует расписание поиска, аналогичное equalRange. Завершает весь поиск за Ο(log(n)) времени.

Примеры:
import std.algorithm.comparison : equal;
auto a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ];
auto r = assumeSorted(a).trisect(3);
assert(equal(r[0], [ 1, 2 ]));
assert(equal(r[1], [ 3, 3, 3 ]));
assert(equal(r[2], [ 4, 4, 5, 6 ]));
bool contains(V)(V value)
Constraints: if (isRandomAccessRange!Range);

Возвращает true тогда и только тогда, когда value можно найти в range, который предполагается отсортированным. Выполняет Ο(log(r.length)) вычислений pred.

bool opBinaryRight(string op, V)(V value)
Constraints: if (op == "in" && isRandomAccessRange!Range);

Подобно contains, но значение указано перед диапазоном.

auto groupBy()();

Возвращает диапазон поддиапазонов элементов, которые эквивалентны согласно отношению сортировки.

auto assumeSorted(alias pred = "a < b", R)(R r)
Constraints: if (isInputRange!(Unqual!R));

Предполагает, что r отсортирован по предикату pred и возвращает соответствующий SortedRange!(pred, R), использующий r в качестве поддержки. Для проверки отсортированности со стоимостью Ο(n) используйте std.algorithm.sorting.isSorted.

Примеры:
import std.algorithm.comparison : equal;

int[] a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
auto p = assumeSorted(a);

assert(equal(p.lowerBound(4), [0, 1, 2, 3]));
assert(equal(p.lowerBound(5), [0, 1, 2, 3, 4]));
assert(equal(p.lowerBound(6), [0, 1, 2, 3, 4, 5]));
assert(equal(p.lowerBound(6.9), [0, 1, 2, 3, 4, 5, 6]));
struct RefRange(R) if (isInputRange!R);

auto refRange(R)(R* range)
Constraints: if (isInputRange!R);

Обёртка, которая эффективно позволяет передавать диапазон по ссылке. И исходный диапазон, и RefRange всегда будут содержать точно такие же элементы. Любое действие, выполненное с одним из них, повлияет на другой. Например, если он передаётся в функцию, которая неявно копирует исходный диапазон при его передаче, исходный диапазон не копируется, а потребляется как тип ссылочного типа.

Примечание
save работает как обычно и обрабатывает новый диапазон, поэтому, если save вызывается с RefRange, любые действия над сохранённым диапазоном не повлияют на исходный.
Параметры:
R* range диапазон для построения RefRange
Возвращаемое значение:
A RefRange. Если заданный диапазон является типом класса (и, следовательно, уже является типом ссылочного типа), то возвращается исходный диапазон, а не RefRange.
Примеры:
Базовый пример
import std.algorithm.searching : find;
ubyte[] buffer = [1, 9, 45, 12, 22];
auto found1 = find(buffer, 45);
writeln(found1); // [45, 12, 22]
writeln(buffer); // [1, 9, 45, 12, 22]

auto wrapped1 = refRange(&buffer);
auto found2 = find(wrapped1, 45);
writeln(*found2.ptr); // [45, 12, 22]
writeln(buffer); // [45, 12, 22]

auto found3 = find(wrapped1.save, 22);
writeln(*found3.ptr); // [22]
writeln(buffer); // [45, 12, 22]

string str = "hello world";
auto wrappedStr = refRange(&str);
writeln(str.front); // 'h'
str.popFrontN(5);
writeln(str); // " world"
writeln(wrappedStr.front); // ' '
writeln(*wrappedStr.ptr); // " world"
Примеры:
Пример opAssign.
ubyte[] buffer1 = [1, 2, 3, 4, 5];
ubyte[] buffer2 = [6, 7, 8, 9, 10];
auto wrapped1 = refRange(&buffer1);
auto wrapped2 = refRange(&buffer2);
assert(wrapped1.ptr is &buffer1);
assert(wrapped2.ptr is &buffer2);
assert(wrapped1.ptr !is wrapped2.ptr);
assert(buffer1 != buffer2);

wrapped1 = wrapped2;

//Everything points to the same stuff as before.
assert(wrapped1.ptr is &buffer1);
assert(wrapped2.ptr is &buffer2);
assert(wrapped1.ptr !is wrapped2.ptr);

//But buffer1 has changed due to the assignment.
writeln(buffer1); // [6, 7, 8, 9, 10]
writeln(buffer2); // [6, 7, 8, 9, 10]

buffer2 = [11, 12, 13, 14, 15];

//Everything points to the same stuff as before.
assert(wrapped1.ptr is &buffer1);
assert(wrapped2.ptr is &buffer2);
assert(wrapped1.ptr !is wrapped2.ptr);

//But buffer2 has changed due to the assignment.
writeln(buffer1); // [6, 7, 8, 9, 10]
writeln(buffer2); // [11, 12, 13, 14, 15]

wrapped2 = null;

//The pointer changed for wrapped2 but not wrapped1.
assert(wrapped1.ptr is &buffer1);
assert(wrapped2.ptr is null);
assert(wrapped1.ptr !is wrapped2.ptr);

//buffer2 is not affected by the assignment.
writeln(buffer1); // [6, 7, 8, 9, 10]
writeln(buffer2); // [11, 12, 13, 14, 15]
pure nothrow @safe this(R* range);
auto opAssign(RefRange rhs);

Это не присваивает указатель rhs этому RefRange. Вместо этого, он присваивает диапазон, на который указывает rhs, диапазону, на который указывает этот RefRange. Это потому, что любое действие над RefRange такое же, как если бы оно происходило с исходным диапазоном. Единственное исключение — когда RefRange присваивается null напрямую или потому, что rhs является null. В этом случае RefRange больше не ссылается на исходный диапазон, а является null.

void opAssign(typeof(null) rhs);
inout pure nothrow @property @safe inout(R*) ptr();

Указатель на обернутый диапазон.

@property auto front();

const @property auto front();

@property auto front(ElementType!R value);
@property bool empty();

const @property bool empty();
void popFront();
@property auto save();

const @property auto save();

auto opSlice();

const auto opSlice();

Определено только если isForwardRange!R является true.

@property auto back();

const @property auto back();

@property auto back(ElementType!R value);

void popBack();

Определено только если isBidirectionalRange!R является true.

ref auto opIndex(IndexType)(IndexType index);

const ref auto opIndex(IndexType)(IndexType index);

Определено только если isRandomAccesRange!R является true.

auto moveFront();

Определено только если hasMobileElements!R и isForwardRange!R являются true.

auto moveBack();

Определено только если hasMobileElements!R и isBidirectionalRange!R являются true.

auto moveAt(size_t index);

Определено только если hasMobileElements!R и isRandomAccessRange!R являются true.

@property size_t length();

const @property size_t length();

alias opDollar = length;

Определено только если hasLength!R является true.

auto opSlice(IndexType1, IndexType2)(IndexType1 begin, IndexType2 end);

const auto opSlice(IndexType1, IndexType2)(IndexType1 begin, IndexType2 end);

Определено только если hasSlicing!R является true.

auto bitwise(R)(auto ref R range)
Constraints: if (isInputRange!R && isIntegral!(ElementType!R));

Битовый адаптер над диапазоном целочисленных типов. Потребляет элементы диапазона побитово, от младшего бита к старшему.

Параметры:
R целочисленный входной диапазон для итерации
R range диапазон для побитовой обработки
Возвращаемое значение:
A Bitwise входной диапазон с распространёнными возможностями прямой, двунаправленной и произвольной итерации
Примеры:
import std.algorithm.comparison : equal;
import std.format : format;

// 00000011 00001001
ubyte[] arr = [3, 9];
auto r = arr.bitwise;

// iterate through it as with any other range
writeln(format("%(%d%)", r)); // "1100000010010000"
assert(format("%(%d%)", r.retro).equal("1100000010010000".retro));

auto r2 = r[5 .. $];
// set a bit
r[2] = 1;
writeln(arr[0]); // 7
writeln(r[5]); // r2[0]
Примеры:
Можно использовать bitwise для реализации однородного генератора bool
import std.algorithm.comparison : equal;
import std.random : rndGen;

auto rb = rndGen.bitwise;
static assert(isInfinite!(typeof(rb)));

auto rb2 = rndGen.bitwise;
// Don't forget that structs are passed by value
assert(rb.take(10).equal(rb2.take(10)));
struct NullSink;

ref auto nullSink();

OutputRange, который отбрасывает получаемые данные.

Примеры:
import std.algorithm.iteration : map;
import std.algorithm.mutation : copy;
[4, 5, 6].map!(x => x * 2).copy(nullSink); // data is discarded
Примеры:
import std.csv : csvNextToken;

string line = "a,b,c";

// ignore the first column
line.csvNextToken(nullSink, ',', '"');
line.popFront;

// look at the second column
Appender!string app;
line.csvNextToken(app, ',', '"');
writeln(app.data); // "b"
auto tee(Flag!"pipeOnPop" pipeOnPop = Yes.pipeOnPop, R1, R2)(R1 inputRange, R2 outputRange)
Constraints: if (isInputRange!R1 && isOutputRange!(R2, ElementType!R1));

auto tee(alias fun, Flag!"pipeOnPop" pipeOnPop = Yes.pipeOnPop, R1)(R1 inputRange)
Constraints: if (is(typeof(fun) == void) || isSomeFunction!fun);

Реализует "tee"-стиль конвейера, оборачивая входной диапазон таким образом, чтобы элементы диапазона могли передаваться в предоставленную функцию или OutputRange по мере итерации. Это полезно для вывода промежуточных значений в длинной цепочке кода диапазонов, выполнения некоторого действия с побочными эффектами при каждом вызове front или popFront, или перенаправления элементов диапазона в вспомогательный OutputRange.

Важно отметить, что поскольку результирующий диапазон оценивается лениво, в случае версии tee, принимающей функцию, функция фактически не будет выполнена до тех пор, пока диапазон не будет "пройден" с помощью функций, оценивающих диапазоны, таких как std.array.array или std.algorithm.iteration.fold.

Параметры:
pipeOnPop Если Yes.pipeOnPop, простое итеративное прохождение диапазона без вызова front достаточно, чтобы tee отражал элементы в outputRange (или, соответственно, fun). Обратите внимание, что каждый вызов popFront() будет отражать старое значение front, а не новое. Это означает, что последнее значение не будет передано, если диапазон не будет итерироваться до пустого состояния. Если No.pipeOnPop, только те элементы, для которых front будет вызван, также будут отправлены в outputRange/fun. Если front вызывается дважды для одного и того же элемента, он всё равно будет отправлен только один раз. Если это кэширование нежелательно, используйте std.algorithm.iteration.map вместо этого.
R1 inputRange Входной диапазон, передаваемый.
R2 outputRange Этот диапазон будет получать элементы inputRange поэтапно по мере продвижения итерации.
fun Эта функция будет вызываться с элементами inputRange поэтапно по мере продвижения итерации.
Возвращаемое значение:
Входной диапазон, предоставляющий элементы inputRange. Независимо от того, является ли inputRange более мощным диапазоном (прямой, двунаправленный и т. д.), результатом всегда является входной диапазон. Чтение этого вызывает итерацию inputRange и возвращает его элементы по очереди. Кроме того, те же элементы будут передаваться в outputRange или fun.
См. также:
std.algorithm.iteration.each
Примеры:
import std.algorithm.comparison : equal;
import std.algorithm.iteration : filter, map;

// Sum values while copying
int[] values = [1, 4, 9, 16, 25];
int sum = 0;
auto newValues = values.tee!(a => sum += a).array;
assert(equal(newValues, values));
writeln(sum); // 1 + 4 + 9 + 16 + 25

// Count values that pass the first filter
int count = 0;
auto newValues4 = values.filter!(a => a < 10)
                        .tee!(a => count++)
                        .map!(a => a + 1)
                        .filter!(a => a < 10);

//Fine, equal also evaluates any lazy ranges passed to it.
//count is not 3 until equal evaluates newValues4
assert(equal(newValues4, [2, 5]));
writeln(count); // 3
auto padLeft(R, E)(R r, E e, size_t n)
Constraints: if ((isInputRange!R && hasLength!R || isForwardRange!R) && !is(CommonType!(ElementType!R, E) == void));

Расширяет длину входного диапазона r, заполняя начало диапазона элементом e. Элемент e должен быть общего типа с типом элемента диапазона r, как определено в std.traits.CommonType. Если n меньше длины r, то r возвращается без изменений.

Если r — это строка с символами Юникода, padLeft следует правилам D относительно длины строк, которая не является количеством символов или графем, а количеством единиц кодирования. Если вы хотите рассматривать каждую графему как имеющую длину только в одну единицу кодирования, вызовите std.uni.byGrapheme перед вызовом этой функции.

Если у r есть длина, то это Ο(1). В противном случае, это Ο(r.length).

Параметры:
R r диапазон ввода с длиной или диапазон прямой итерации
E e элемент для заполнения диапазона
size_t n длина для заполнения
Возвращает:
Диапазон, содержащий элементы исходного диапазона с дополнительным заполнением. См. также: std.string.leftJustifier
Примеры:
import std.algorithm.comparison : equal;

assert([1, 2, 3, 4].padLeft(0, 6).equal([0, 0, 1, 2, 3, 4]));
assert([1, 2, 3, 4].padLeft(0, 3).equal([1, 2, 3, 4]));

assert("abc".padLeft('_', 6).equal("___abc"));
auto padRight(R, E)(R r, E e, size_t n)
Constraints: if (isInputRange!R && !isInfinite!R && !is(CommonType!(ElementType!R, E) == void));

Расширяет длину входного диапазона r, заполняя конец диапазона элементом e. Элемент e должен быть общего типа с типом элемента диапазона r, как определено в std.traits.CommonType. Если n меньше длины r, то содержимое r возвращается.

Базовые функции диапазона, которые предоставляет результирующий диапазон, зависят от того, предоставляют ли их r. За исключением функций back и popBack, которые также требуют, чтобы диапазон имел длину, а также back и popBack

Параметры:
R r диапазон ввода с длиной
E e элемент для заполнения диапазона
size_t n длина для заполнения
Возвращает:
Диапазон, содержащий элементы исходного диапазона с дополнительным заполнением. См. также: std.string.rightJustifier
Примеры:
import std.algorithm.comparison : equal;

assert([1, 2, 3, 4].padRight(0, 6).equal([1, 2, 3, 4, 0, 0]));
assert([1, 2, 3, 4].padRight(0, 4).equal([1, 2, 3, 4]));

assert("abc".padRight('_', 6).equal("abc___"));

© 1999–2021 The D Language Foundation
Licensed under the Boost License 1.0.
https://dlang.org/phobos/std_range.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API