std.algorithm.mutation

Переместиться к: bringToFront · copy · fill · initializeAll · move · moveAll · moveEmplace · moveEmplaceAll · moveEmplaceSome · moveSome · remove · reverse · strip · stripLeft · stripRight · swap · swapAt · swapRanges · SwapStrategy · uninitializedFill

Это подмодуль модуля std.algorithm. Он содержит типовые алгоритмы изменения.
Шпаргалка
Имя функции Описание
bringToFront Если a = [1, 2, 3] и b = [4, 5, 6, 7], bringToFront(a, b) оставит a = [4, 5, 6] и b = [7, 1, 2, 3].
copy Копирует диапазон в другой. Если a = [1, 2, 3] и b = new int[5], тогда copy(a, b) оставит b = [1, 2, 3, 0, 0] и возвратит b[3 .. $].
fill Заполняет диапазон образцом, например, если a = new int[3], тогда fill(a, 4) оставит a = [4, 4, 4], а fill(a, [3, 4]) оставит a = [3, 4, 3].
initializeAll Если a = [1.2, 3.4], тогда initializeAll(a) оставит a = [double.init, double.init].
move move(a, b) перемещает a в b. move(a) читает a разрушительно при необходимости.
moveAll Перемещает все элементы из одного диапазона в другой.
moveSome Перемещает так много элементов, сколько возможно, из одного диапазона в другой.
remove Удаляет элементы из диапазона на-месте и возвращает укороченный диапазон.
reverse Если a = [1, 2, 3], reverse(a) изменит его в [3, 2, 1].
strip Удаляет все начальные и конечные элементы, равные значению, или удовлетворяющие предикату. Если a = [1, 1, 0, 1, 1], тогда strip(a, 1) и strip!(e => e == 1)(a) возвратят [0].
stripLeft Удаляет все начальные элементы, равные значению, или удовлетворяющие предикату. Если a = [1, 1, 0, 1, 1], тогда stripLeft(a, 1) и stripLeft!(e => e == 1)(a) возвратят [0, 1, 1].
stripRight Удаляет все конечные элементы, равные значению, или удовлетворяющие предикату. Если a = [1, 1, 0, 1, 1], тогда stripRight(a, 1) и stripRight!(e => e == 1)(a) возвратят [1, 1, 0].
swap Меняет два значения.
swapAt Меняет два значения по индексам.
swapRanges Меняет все элементы двух диапазонов.
uninitializedFill Заполняет значением диапазон (предполагаемый неинициализированным).
Лицензия:
Boost License 1.0.
Авторы:
Andrei Alexandrescu

Исходный код: std/algorithm/mutation.d

size_t bringToFront(Range1, Range2)(Range1 front, Range2 back)
if (isInputRange!Range1 && isForwardRange!Range2);
Функция bringToFront довольно гибкая и полезная. Она может вращать элементы в одном буфере влево или вправо, менять буферы равной длины, и даже перемещать элементы через непересекающиеся буферы разных типов и разной длины.
bringToFront принимает два диапазона front и back, которые могут быть различных типов. Рассматривая конкатенацию front и back как один объединённый диапазон, bringToFront сдвигает объединённый диапазон так, что все элементы из back переходят в начало объединённого диапазона. Относительное упорядочение элементов в front и back, соответственно, остается неизменным.
Выполняется за Ο(max(front.length, back.length)) операций обмена.

Предварительные условия: Либо front и back не пересекаются, либо back достижима из front, и front не достижим из back.

Параметры:
Range1 front входной диапазон
Range2 back лидирующий диапазон
Возвращает:
Количество элементов, перенесённых во front, то есть, длина back.
Смотрите также:
Примеры:
Самое простое применение bringToFront – это вращение элементов в буфере. Например:
auto arr = [4, 5, 6, 7, 1, 2, 3];
auto p = bringToFront(arr[0 .. 4], arr[4 .. $]);
assert(p == arr.length - 4);
assert(arr == [ 1, 2, 3, 4, 5, 6, 7 ]);
Примеры:
Диапазон front действительно может "переступить" диапазон back. Это очень полезно с лидирующими диапазонами, которые не могут удобно вычислять ограниченные справа поддиапазоны, подобно arr[0 .. 4] в предыдущем примере. В примере ниже, r2 является правым поддиапазоном r1.
import std.algorithm.comparison : equal;
import std.container : SList;

auto list = SList!(int)(4, 5, 6, 7, 1, 2, 3);
auto r1 = list[];
auto r2 = list[]; popFrontN(r2, 4);
assert(equal(r2, [ 1, 2, 3 ]));
bringToFront(r1, r2);
assert(equal(list[], [ 1, 2, 3, 4, 5, 6, 7 ]));
Примеры:
Элементы могут меняться через диапазоны различных типов:
import std.algorithm.comparison : equal;
import std.container : SList;

auto list = SList!(int)(4, 5, 6, 7);
auto vec = [ 1, 2, 3 ];
bringToFront(list[], vec);
assert(equal(list[], [ 1, 2, 3, 4 ]));
assert(equal(vec, [ 5, 6, 7 ]));
TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target)
if (areCopyCompatibleArrays!(SourceRange, TargetRange));

TargetRange copy(SourceRange, TargetRange)(SourceRange source, TargetRange target)
if (!areCopyCompatibleArrays!(SourceRange, TargetRange) && isInputRange!SourceRange && isOutputRange!(TargetRange, ElementType!SourceRange));
Копирует содержимое источника source в цель target и возвращает остальную (незаполненную) часть цели.

Предварительные условия: Цель target должна иметь достаточно места, чтобы разместить весь источник целиком.

Параметры:
SourceRange source входной диапазон
TargetRange target выходной диапазон
Возвращает:
Незаполненную часть target
Смотрите также:
Примеры:
int[] a = [ 1, 5 ];
int[] b = [ 9, 8 ];
int[] buf = new int[](a.length + b.length + 10);
auto rem = a.copy(buf);    // копирование a в buf
rem = b.copy(rem);         // копирование b в оставшуюся часть buf
assert(buf[0 .. a.length + b.length] == [1, 5, 9, 8]);
assert(rem.length == 10);   // неиспользуемое место в buf
Примеры:
До тех пор, пока элементы диапазона target поддерживают присвоение от элементов диапазона source, принимаются различные типы диапазонов:
float[] src = [ 1.0f, 5 ];
double[] dest = new double[src.length];
src.copy(dest);
Примеры:
Чтобы скопировать самое большее n элементов из диапазона, вы можете использовать std.range.take:
import std.range;
int[] src = [ 1, 5, 8, 9, 10 ];
auto dest = new int[](3);
src.take(dest.length).copy(dest);
assert(dest == [ 1, 5, 8 ]);
Примеры:
Чтобы скопировать только те элементы из диапазона, которые удовлетворяют предикату, используйте filter:
import std.algorithm.iteration : filter;
int[] src = [ 1, 5, 8, 9, 10, 1, 2, 0 ];
auto dest = new int[src.length];
auto rem = src
    .filter!(a => (a & 1) == 1)
    .copy(dest);
assert(dest[0 .. $ - rem.length] == [ 1, 5, 9, 1 ]);
Примеры:
std.range.retro можно использовать, чтобы получить поведение, подобное copy_backward в STL:
import std.algorithm, std.range;
int[] src = [1, 2, 4];
int[] dest = [0, 0, 0, 0, 0];
src.retro.copy(dest.retro);
assert(dest == [0, 0, 1, 2, 4]);

Переместиться к: 2

void fill(Range, Value)(Range range, Value value)
if (isInputRange!Range && is(typeof(range.front = value)));
Присваивает значение value каждому элементу входного диапазона range.
Параметры:
Range range Входной диапазон, который выводит наружу ссылки на свои элементы, и его элементы поддерживают присвоение
Value value Значение, присваиваемое каждому элементу диапазона
Смотрите также:
Примеры:
int[] a = [ 1, 2, 3, 4 ];
fill(a, 5);
assert(a == [ 5, 5, 5, 5 ]);
void fill(Range1, Range2)(Range1 range, Range2 filler)
if (isInputRange!Range1 && (isForwardRange!Range2 || isInputRange!Range2 && isInfinite!Range2) && is(typeof(Range1.init.front = Range2.init.front)));
Заполняет диапазон range образцом, копируемым из заполнителя filler. Длина диапазона не обязана быть кратной длине заполнителя. Если заполнитель пустой, бросается исключение.
Параметры:
Range1 range Входной диапазон, который выводит наружу ссылки на свои элементы, и его элементы поддерживают присвоение
Range2 filler Лидирующий диапазон, который представляет заполняющий образец.
Примеры:
int[] a = [ 1, 2, 3, 4, 5 ];
int[] b = [ 8, 9 ];
fill(a, b);
assert(a == [ 8, 9, 8, 9, 8 ]);
void initializeAll(Range)(Range range)
if (isInputRange!Range && hasLvalueElements!Range && hasAssignableElements!Range);

void initializeAll(Range)(Range range)
if (is(Range == char[]) || is(Range == wchar[]));
Инициализирует все элементы дипазона их .init-значением. Принимает, что элементы диапазона неинициализированы.
Параметры:
Range range Входной диапазон, который выводит наружу ссылки на свои элементы, и его элементы поддерживают присвоение
Смотрите также:
Примеры:
import core.stdc.stdlib : malloc, free;

struct S
{
    int a = 10;
}

auto s = (cast(S*) malloc(5 * S.sizeof))[0 .. 5];
initializeAll(s);
assert(s == [S(10), S(10), S(10), S(10), S(10)]);

scope(exit) free(s.ptr);
void move(T)(ref T source, ref T target);

T move(T)(ref T source);
Перемещение источника source в цель target, через разрушающее копирование при необходимости.
Если T – структура с деструктором или определённым postblit, source восстанавливается в своё .init-значение после перемещения в target, в противном случае оставляется неизменным.

Предварительные условия: Если source имеет внутренние указатели, которые указывают на себя, он не может быть перемещён, и будет инициирована ошибка.

Параметры:
T source Данные для копирования.
T target Куда копировать. Деструктор, если имеется в наличии, вызывается перед выполнением копирования.
Примеры:
Для типов, не являющихся структурами, move просто выполняет target = source:
Object obj1 = new Object;
Object obj2 = obj1;
Object obj3;

move(obj2, obj3);
assert(obj3 is obj1);
// obj2 не изменился
assert(obj2 is obj1);
Примеры:
// Структуры без деструкторов просто копируются
struct S1
{
    int a = 1;
    int b = 2;
}
S1 s11 = { 10, 11 };
S1 s12;

move(s11, s12);

assert(s12 == S1(10, 11));
assert(s11 == s12);

// Но структуры с деструкторами или с postblits восстанавливают своё
// .init-значение после копирования в target.
struct S2
{
    int a = 1;
    int b = 2;

    ~this() pure nothrow @safe @nogc { }
}
S2 s21 = { 3, 4 };
S2 s22;

move(s21, s22);

assert(s21 == S2(1, 2));
assert(s22 == S2(3, 4));
Примеры:
Не-копируемые структуры всё ещё могут перемещаться:
struct S
{
    @disable this(this);
    ~this() pure nothrow @safe @nogc {}
}
S s1;
S s2 = move(s1);
@system void moveEmplace(T)(ref T source, ref T target);
Аналогично move, но принимает, что цель target неинициализирована. Это более эффективно, поскольку источник source может быть перенесён на цель без предварительного уничтожения или инициализации.
Параметры:
T source значение, перемещаемое в target
T target неинициализированное значение, которое будет заполнено источником source
Примеры:
static struct Foo
{
pure nothrow @nogc:
    this(int* ptr) { _ptr = ptr; }
    ~this() { if (_ptr) ++*_ptr; }
    int* _ptr;
}

int val;
Foo foo1 = void; // неинициализировано
auto foo2 = Foo(&val); // инициализировано
assert(foo2._ptr is &val);

// Использование `move(foo2, foo1)` будет иметь непредсказуемый эффект, потому что вызовет деструктор
// неинициализированного foo1.
// moveEmplace непосредственно перезаписывает foo1, без уничтожения или инициализации его перед этим.
moveEmplace(foo2, foo1);
assert(foo1._ptr is &val);
assert(foo2._ptr is null);
assert(val == 0);
Range2 moveAll(Range1, Range2)(Range1 src, Range2 tgt)
if (isInputRange!Range1 && isInputRange!Range2 && is(typeof(move(src.front, tgt.front))));
Вызывает move(a, b) для каждого элемента a в src и соответствующего элемента b в tgt, в увеличивающемся порядке.

Предварительные условия: walkLength(src) <= walkLength(tgt). Это предусловие проверяется в assert. Если вы не можете гарантировать наличие достаточного пространства в tgt для помещения всего src, используйте вместо этой функции moveSome.
Вот не согласен я с этим assert. Вполне реальный повод вызвать настоящее исключение. – прим. пер.

Параметры:
Range1 src Входной диапазон с перемещаемыми элементами.
Range2 tgt Входной диапазон с элементами, в которые будут перемещены элементы из src.
Возвращает:
Оставшаяся часть tgt после перемещённых элементов из src.
Примеры:
int[3] a = [ 1, 2, 3 ];
int[5] b;
assert(moveAll(a[], b[]) is b[3 .. $]);
assert(a[] == b[0 .. 3]);
int[3] cmp = [ 1, 2, 3 ];
assert(a[] == cmp[]);
@system Range2 moveEmplaceAll(Range1, Range2)(Range1 src, Range2 tgt)
if (isInputRange!Range1 && isInputRange!Range2 && is(typeof(moveEmplace(src.front, tgt.front))));
Подобно moveAll, но принимает, что все элементы в tgt неинициализированы. Использует moveEmplace для перемещения элементов из src поверх элементов в tgt.
Примеры:
static struct Foo
{
    ~this() pure nothrow @nogc { if (_ptr) ++*_ptr; }
    int* _ptr;
}
int[3] refs = [0, 1, 2];
Foo[3] src = [Foo(&refs[0]), Foo(&refs[1]), Foo(&refs[2])];
Foo[5] dst = void;

auto tail = moveEmplaceAll(src[], dst[]); // перемещает 3 значения из src поверх dst
assert(tail.length == 2); // возвращает оставшиеся неинициализированные величины
initializeAll(tail);

import std.algorithm.searching : all;
assert(src[].all!(e => e._ptr is null));
assert(dst[0 .. 3].all!(e => e._ptr !is null));
Tuple!(Range1, Range2) moveSome(Range1, Range2)(Range1 src, Range2 tgt)
if (isInputRange!Range1 && isInputRange!Range2 && is(typeof(move(src.front, tgt.front))));
Вызывает move(a, b) для каждого элемента a в src и соответствующего элемента b в tgt, в увеличивающемся порядке, останавливаясь, когда любой диапазон исчерпается.
Параметры:
Range1 src Входной диапазон с перемещаемыми элементами.
Range2 tgt Входной диапазон с элементами, которые замещаются элементами из src.
Возвращает:
Оставшиеся части двух диапазонов после того, как один или другой из диапазонов был исчерпан.
Примеры:
int[5] a = [ 1, 2, 3, 4, 5 ];
int[3] b;
assert(moveSome(a[], b[])[0] is a[3 .. $]);
assert(a[0 .. 3] == b);
assert(a == [ 1, 2, 3, 4, 5 ]);
@system Tuple!(Range1, Range2) moveEmplaceSome(Range1, Range2)(Range1 src, Range2 tgt)
if (isInputRange!Range1 && isInputRange!Range2 && is(typeof(move(src.front, tgt.front))));
То же, что moveSome, но принимает, что все элементы в tgt неинициализированы. Использует moveEmplace для перемещения элементов из src поверх элементов в tgt.
Примеры:
static struct Foo
{
    ~this() pure nothrow @nogc { if (_ptr) ++*_ptr; }
    int* _ptr;
}
int[4] refs = [0, 1, 2, 3];
Foo[4] src = [Foo(&refs[0]), Foo(&refs[1]), Foo(&refs[2]), Foo(&refs[3])];
Foo[3] dst = void;

auto res = moveEmplaceSome(src[], dst[]);

import std.algorithm.searching : all;
assert(src[0 .. 3].all!(e => e._ptr is null));
assert(src[3]._ptr !is null);
assert(dst[].all!(e => e._ptr !is null));

Переместиться к: semistable · stable · unstable

enum SwapStrategy: int;
Определяет стратегию обмена для алгоритмов, которым нужно обменивать элементы в диапазоне (такие, как например, partition и sort). Стратегия касается обмена элементов, которые не являются основной заботой алгоритма. Например, рассмотрим алгоритм, который сортирует [ "abc", "b", "aBc" ] согласно предикату toUpper(a) < toUpper(b). Этот алгоритм может выбрать поменять местами две эквивалентные строки "abc" и "aBc". Это не влияет на сортировку, так как [ "abc", "aBc", "b" ] и [ "aBc", "abc", "b" ] являются допустимыми результатами.
Некоторые ситуации требуют, чтобы алгоритм НЕ изменял когда-либо относительное упорядочение эквиалентных элементов (в примере выше, только [ "abc", "aBc", "b" ] будет правильным результатом). Такие алгоритмы называются стабильными (stable). Если упорядочивающий алгоритм может по своему усмотрению поменять эквивалентные элементы, упорядочение называется нестабильным (unstable).
Еще один класс алгоритмов может выбрать промежуточный компромисс, будучи стабильным только на хорошо определенном поддиапазоне диапазона. Не существует установленной терминологии для такого поведения; в этой библиотеке оно называется полустабильным (semistable).
Обычно, стратегия стабильного упорядочения может быть более дорогой по времени и/или используемой памяти, чем две остальных, поскольку она навязывает дополнительные ограничения. Аналогично, полустабильная может быть дороже, чем нестабильная. Так как (полу-)стабильность нужна не очень часто, упорядочивающие алгоритмы в этом модуле, параметризованные SwapStrategy, всегда используют SwapStrategy.unstable по умолчанию.
unstable
Допускает свободный обмен элементов до тех пор, пока результат удовлетворяет требованиям алгоритма.
semistable
В алгоритмах, разделяющих диапазоны на два, сохраняет относительное упорядочение элементов только слева от точки разделения.
stable
Сохраняет относительное упорядочение элементов в наибольшей степени, допускаемой требованиями алгоритма.

Переместиться к: 2

Range remove(SwapStrategy s = SwapStrategy.stable, Range, Offset...)(Range range, Offset offset)
if (s != SwapStrategy.stable && isBidirectionalRange!Range && hasLvalueElements!Range && hasLength!Range && Offset.length >= 1);

Range remove(SwapStrategy s = SwapStrategy.stable, Range, Offset...)(Range range, Offset offset)
if (s == SwapStrategy.stable && isBidirectionalRange!Range && hasLvalueElements!Range && Offset.length >= 1);
Удаляет элементы по данному смещению offset из диапазона range и возвращает сокращённый диапазон. При самом простом вызове, удаляется один элемент.
int[] a = [ 3, 5, 7, 8 ];
assert(remove(a, 1) == [ 3, 7, 8 ]);
assert(a == [ 3, 7, 8, 8 ]);
В случае вверху элемент со смещением 1 удаляется, и remove возвращает диапазон, уменьшенный на один элемент. Исходный массив остался той же самой длины, поскольку все функции в std.algorithm измененяют только содержимое, а не топологию. Значение 8 повторилось, поскольку для перемещения элементов была задействована функция move , а при перемещении целых источник просто копируется в назначение. Чтобы заменить a эффектом удаления, просто присвойте a = remove(a, 1). Срез будет повторно привязан к укокороченному массиву и операция завершается с максимальной эффективностью.
В remove можно передать множество индексов. В этом случае, элементы с соответствующими индексами полностью удаляются. Индексы должны передаваться в возрастающем порядке, в противном случае вызывается исключение.
int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
assert(remove(a, 1, 3, 5) ==
    [ 0, 2, 4, 6, 7, 8, 9, 10 ]);
(Заметьте, что все индексы ссылаются на места в исходном массиве, а не в массиве, который прогрессивно сокращается.)
Наконец, можно передавать любую комбинацию целых смещений и кортежей, сформированных из двух целых смещений.
int[] a = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
assert(remove(a, 1, tuple(3, 5), 9) == [ 0, 2, 5, 6, 7, 8, 10 ]);
В этом случае, места в позициях 1, 3, 4, и 9 удалены из массива. Кортеж передаётся в диапазон закрытым слева и открытым справа (соответствует встроенным срезам), например, tuple(3, 5) означает индексы 3 и 4, но не 5.
Если необходимо удалить некоторые элементы в диапазоне, но порядок остальных элементов не обязательно сохранять, вы можете захотеть передать в remove параметр SwapStrategy.unstable.
int[] a = [ 0, 1, 2, 3 ];
assert(remove!(SwapStrategy.unstable)(a, 1) == [ 0, 3, 2 ]);
В случае выше, элемент с индексом 1 удалён, но заменён последним элементом диапазона. Воспользовавшись ослаблением требования стабильности, remove переместила элементы из конца массива в места, предназначенные для удаления. Таким образом, данные меньше перемещались, что улучшило время выполнения функции.
Функция remove работает на двунаправленных диапазонах, которые содержат присваиваемые lvalue-элементы. Вот стратегия перемещения (перечислено от самой быстрой до самой медленной):
  • Если s == SwapStrategy.unstable && isRandomAccessRange!Range && hasLength!Range && hasLvalueElements!Range, то элементы перемещаются из конца диапазона в места для заполнения. В этом случае выполняется абсолютный минимум ходов.
  • В противном случае, если s == SwapStrategy.unstable && isBidirectionalRange!Range && hasLength!Range && hasLvalueElements!Range, то элементы все еще перемещаются из конца диапазона, но тратится время на прохождение между местами удаления через регулярные вызовы range.popFront.
  • В противном случае, элементы перемещаются постепенно в направлении передней части диапазона; данный элемент никогда не перемещается несколько раз, но перемещается большее количество элементов, чем в предыдущих случаях.
Параметры:
s Стратегия обмена, определяющая, необходимо ли сохранить изначальный порядок
Range range Двунаправленный диапазон с примитивом длины length
Offset offset какой(ие) элемент(ы) удалять
Возвращает:
диапазон, содержащий все элементы диапазона range после удаления offset
Range remove(alias pred, SwapStrategy s = SwapStrategy.stable, Range)(Range range)
if (isBidirectionalRange!Range && hasLvalueElements!Range);
Уменьшает длину двунаправленного диапазона range range, удаляя элементы, которые удовлетворяют pred. Если s = SwapStrategy.unstable, элементы перемещаются из правого конца на место удаляемых элементов. Если s = SwapStrategy.stable (по-умолчанию), элементы перемещаются прогрессивно к началу так, так что их относительный порядок сохраняется. Возвращает отфильтрованный диапазон.
Параметры:
Range range двунаправленный диапазон с lvalue-элементами
Возвращает:
диапазон, у которого удалены все элементами, для которых pred возвращает истину
Примеры:
static immutable base = [1, 2, 3, 2, 4, 2, 5, 2];

int[] arr = base[].dup;

// использование строкового предиката
assert(remove!("a == 2")(arr) == [ 1, 3, 4, 5 ]);

// Оригинальное содержание массива модифицировано,
// так что нам нужно восстановить его в исходное состояние.
// Тем не менее, длина не изменилась.
arr[] = base[];

// использование лямбда-предиката
assert(remove!(a => a == 2)(arr) == [ 1, 3, 4, 5 ]);

Переместиться к: 2

void reverse(Range)(Range r)
if (isBidirectionalRange!Range && !isRandomAccessRange!Range && hasSwappableElements!Range);

void reverse(Range)(Range r)
if (isRandomAccessRange!Range && hasLength!Range);
Переворачивает r на-месте. Выполняет r.length / 2 операций обмена.
Параметры:
Range r двунаправленный диапазон со сменяемыми элементами или диапазон произвольным доступом со свойством длины length
Смотрите также:
Примеры:
int[] arr = [ 1, 2, 3 ];
reverse(arr);
assert(arr == [ 3, 2, 1 ]);
void reverse(Char)(Char[] s)
if (isNarrowString!(Char[]) && !is(Char == const) && !is(Char == immutable));
Переворачивает r на-месте, где r является узкой строкой (состоит из элементов типа char или wchar). UTF-последовательности, состоящие из нескольких code units сохраняются должным образом.
Параметры:
Char[] s узкая строка
Недостатки:
При прохождении по символам с модификаторами уникода, например \u0301, эта функция не правильно сохранит позицию модификатора. Например, переворот ba\u0301d ("bád") даст результат d\u0301ab ("d́ab") вместо da\u0301b ("dáb").
Примеры:
char[] arr = "hello\U00010143\u0100\U00010143".dup;
reverse(arr);
assert(arr == "\U00010143\u0100\U00010143olleh");
Range strip(Range, E)(Range range, E element)
if (isBidirectionalRange!Range && is(typeof(range.front == element) : bool));

Range strip(alias pred, Range)(Range range)
if (isBidirectionalRange!Range && is(typeof(pred(range.back)) : bool));

Range stripLeft(Range, E)(Range range, E element)
if (isInputRange!Range && is(typeof(range.front == element) : bool));

Range stripLeft(alias pred, Range)(Range range)
if (isInputRange!Range && is(typeof(pred(range.front)) : bool));

Range stripRight(Range, E)(Range range, E element)
if (isBidirectionalRange!Range && is(typeof(range.back == element) : bool));

Range stripRight(alias pred, Range)(Range range)
if (isBidirectionalRange!Range && is(typeof(pred(range.back)) : bool));
Группа функций strip позволяет удалить передние, конечные, или как передние, так и конечные элементы element.
Функция stripLeft зачистит переднюю сторону диапазона, функция stripRight зачистит конечную сторону, тогда как функция strip зачистит как переднюю, так и конечную сторону диапазона.
Заметьте, что функции strip и stripRight требуют, чтобы range был Двунаправленным диапазоном.
Все эти функции имеют по две разновидности: одна принимает целевой элемент element, здесь диапазон зачищается до тех пор, пока этот элемент обнаруживается. Другая принимает лямбда-предикат, здесь диапазон зачищается до тех пор, пока предикат возвращает истину.
Параметры:
Range range двунаправленный или входной диапазон
E element удаляемые элементы
Возвращает:
Весь диапазон, исключая элемент element в начале и конце
Примеры:
Удалить начальные и конечные элементы, равные целевому элементу.
assert("  foobar  ".strip(' ') == "foobar");
assert("00223.444500".strip('0') == "223.4445");
assert("ëëêéüŗōpéêëë".strip('ë') == "êéüŗōpéê");
assert([1, 1, 0, 1, 1].strip(1) == [0]);
assert([0.0, 0.01, 0.01, 0.0].strip(0).length == 2);
Примеры:
Удалять начальные и конечные элементы, пока предикат возвращает true.
assert("  foobar  ".strip!(a => a == ' ')() == "foobar");
assert("00223.444500".strip!(a => a == '0')() == "223.4445");
assert("ëëêéüŗōpéêëë".strip!(a => a == 'ë')() == "êéüŗōpéê");
assert([1, 1, 0, 1, 1].strip!(a => a == 1)() == [0]);
assert([0.0, 0.01, 0.5, 0.6, 0.01, 0.0].strip!(a => a < 0.4)().length == 2);
Примеры:
Удалить начальные элементы, равные целевому элементу.
assert("  foobar  ".stripLeft(' ') == "foobar  ");
assert("00223.444500".stripLeft('0') == "223.444500");
assert("ůůűniçodêéé".stripLeft('ů') == "űniçodêéé");
assert([1, 1, 0, 1, 1].stripLeft(1) == [0, 1, 1]);
assert([0.0, 0.01, 0.01, 0.0].stripLeft(0).length == 3);
Примеры:
Удалять начальные элементы, пока предикат возвращает true.
assert("  foobar  ".stripLeft!(a => a == ' ')() == "foobar  ");
assert("00223.444500".stripLeft!(a => a == '0')() == "223.444500");
assert("ůůűniçodêéé".stripLeft!(a => a == 'ů')() == "űniçodêéé");
assert([1, 1, 0, 1, 1].stripLeft!(a => a == 1)() == [0, 1, 1]);
assert([0.0, 0.01, 0.10, 0.5, 0.6].stripLeft!(a => a < 0.4)().length == 2);
Примеры:
Удалить конечные элементы, равные целевому элементу.
assert("  foobar  ".stripRight(' ') == "  foobar");
assert("00223.444500".stripRight('0') == "00223.4445");
assert("ùniçodêéé".stripRight('é') == "ùniçodê");
assert([1, 1, 0, 1, 1].stripRight(1) == [1, 1, 0]);
assert([0.0, 0.01, 0.01, 0.0].stripRight(0).length == 3);
Примеры:
Удалять конечные элементы, пока предикат возвращает true.
assert("  foobar  ".stripRight!(a => a == ' ')() == "  foobar");
assert("00223.444500".stripRight!(a => a == '0')() == "00223.4445");
assert("ùniçodêéé".stripRight!(a => a == 'é')() == "ùniçodê");
assert([1, 1, 0, 1, 1].stripRight!(a => a == 1)() == [1, 1, 0]);
assert([0.0, 0.01, 0.10, 0.5, 0.6].stripRight!(a => a > 0.4)().length == 3);
pure nothrow @nogc @trusted void swap(T)(ref T lhs, ref T rhs)
if (isBlitAssignable!T && !is(typeof(lhs.proxySwap(rhs))));

void swap(T)(ref T lhs, ref T rhs)
if (is(typeof(lhs.proxySwap(rhs))));
Обменивает lhs и rhs. Экземпляры lhs и rhs перемещаются в памяти, никогда не вызывая ни opAssign, ни какой-либо другой функции. T не обязан быть присваиваемым для обмена.
Если lhs и rhs – это ссылки на один и тот же экземпляр, тогда ничего не делается.
lhs и rhs должны быть mutable. Если T – структура или объединение, тогда все поля также должны быть mutable (рекурсивно).
Параметры:
T lhs Данные, которые нужно обменять с rhs.
T rhs Данные, которые нужно обменять с lhs.
Примеры:
// Обмен типов POD (простые старые данные):
int a = 42, b = 34;
swap(a, b);
assert(a == 34 && b == 42);

// Обмен структур с косвенностью:
static struct S { int x; char c; int[] y; }
S s1 = { 0, 'z', [ 1, 2 ] };
S s2 = { 42, 'a', [ 4, 6 ] };
swap(s1, s2);
assert(s1.x == 42);
assert(s1.c == 'a');
assert(s1.y == [ 4, 6 ]);

assert(s2.x == 0);
assert(s2.c == 'z');
assert(s2.y == [ 1, 2 ]);

// Неизменняемые данные нельзя обменивать:
immutable int imm1, imm2;
static assert(!__traits(compiles, swap(imm1, imm2)));
Примеры:
// Не-копируемые типы всё ещё можно менять.
static struct NoCopy
{
    this(this) { assert(0); }
    int n;
    string s;
}
NoCopy nc1, nc2;
nc1.n = 127; nc1.s = "abc";
nc2.n = 513; nc2.s = "uvwxyz";

swap(nc1, nc2);
assert(nc1.n == 513 && nc1.s == "uvwxyz");
assert(nc2.n == 127 && nc2.s == "abc");

swap(nc1, nc1);
swap(nc2, nc2);
assert(nc1.n == 513 && nc1.s == "uvwxyz");
assert(nc2.n == 127 && nc2.s == "abc");

// Типы, содержащие не-копируемые поля также можно обменивать.
static struct NoCopyHolder
{
    NoCopy noCopy;
}
NoCopyHolder h1, h2;
h1.noCopy.n = 31; h1.noCopy.s = "abc";
h2.noCopy.n = 65; h2.noCopy.s = null;

swap(h1, h2);
assert(h1.noCopy.n == 65 && h1.noCopy.s == null);
assert(h2.noCopy.n == 31 && h2.noCopy.s == "abc");

swap(h1, h1);
swap(h2, h2);
assert(h1.noCopy.n == 65 && h1.noCopy.s == null);
assert(h2.noCopy.n == 31 && h2.noCopy.s == "abc");

// Типы с const нельзя обменивать.
const NoCopy const1, const2;
static assert(!__traits(compiles, swap(const1, const2)));
void swapAt(R)(auto ref R r, size_t i1, size_t i2);
Меняет на-месте в диапазоне r два элемента, определённых своими индексами i1 и i2.
Параметры:
R r диапазон с обмениваемыми элементами
size_t i1 первый индекс
size_t i2 второй индекс
Примеры:
import std.algorithm.comparison : equal;
auto a = [1, 2, 3];
a.swapAt(1, 2);
assert(a.equal([1, 3, 2]));
Tuple!(Range1, Range2) swapRanges(Range1, Range2)(Range1 r1, Range2 r2)
if (isInputRange!Range1 && isInputRange!Range2 && hasSwappableElements!Range1 && hasSwappableElements!Range2 && is(ElementType!Range1 == ElementType!Range2));
Меняет последовательно все элементы r1 с элементами в r2. Возвращает кортеж, содержащий оставшиеся части r1 и r2, которые не были заменены (один из них будет пустым). Диапазоны могут быть разных типов, но должны иметь один и тот же тип элементов, и поддерживать обмен.
Параметры:
Range1 r1 входной диапазон с обмениваемыми элементами
Range2 r2 входной диапазон с обмениваемыми элементами
Возвращает:
Кортеж, содержащий оставшиеся части r1 и r2, которые не были заменены
Примеры:
int[] a = [ 100, 101, 102, 103 ];
int[] b = [ 0, 1, 2, 3 ];
auto c = swapRanges(a[1 .. 3], b[2 .. 4]);
assert(c[0].empty && c[1].empty);
assert(a == [ 100, 2, 3, 103 ]);
assert(b == [ 0, 1, 101, 102 ]);
void uninitializedFill(Range, Value)(Range range, Value value)
if (isInputRange!Range && hasLvalueElements!Range && is(typeof(range.front = value)));
Инициализирует каждый элемент диапазона значением value. Предполагает, что элементы диапазона неинициализированны. Это представляет интерес для структур, которые определяют конструкторы копирования (для всех остальных типов, fill и uninitializedFill эквивалентны).
Параметры:
Range range Входной диапазон, который выводит наружу ссылки на свои элементы и содержит присваиваемые элементы
Value value Присваивается каждому элементу диапазона
Смотрите также:
Примеры:
import core.stdc.stdlib : malloc, free;

auto s = (cast(int*) malloc(5 * int.sizeof))[0 .. 5];
uninitializedFill(s, 42);
assert(s == [ 42, 42, 42, 42, 42 ]);

scope(exit) free(s.ptr);