Переместиться к: cache · cacheBidirectional · chunkBy · cumulativeFold · each · filter · filterBidirectional · fold · group · Group · joiner · map · permutations · Permutations · reduce · splitter · sum · uniq
Имя функции | Описание |
---|---|
cache | Eagerly evaluates and caches another range's front. Жадно вычисляет и кеширует front в другом диапазоне. |
cacheBidirectional | Тоже, что и выше, но также предоставляет back и popBack. |
chunkBy | chunkBy!((a,b) => a[1] == b[1])([[1, 1], [1, 2], [2, 2], [2, 1]]) возвращает диапазон, содержащий 3 поддиапазона: первый с [1, 1]; второй с элементами [1, 2] и [2, 2]; и третий с [2, 1]. |
cumulativeFold | cumulativeFold!((a, b) => a + b)([1, 2, 3, 4]) возвращает лениво-вычисляемый диапазон, содержащий последовательно сведённые (reduced) значения 1, 3, 6, 10. |
each | each!writeln([1, 2, 3]) жадно печатает числа 1, 2 и 3 на своих собственных строках. |
filter | filter!(a => a > 0)([1, -1, 2, 0, -3]) итерирует элементы 1 и 2. |
filterBidirectional | Аналогично filter, но также предоставляет back и popBack при небольшом увеличении стоимости. |
fold | fold!((a, b) => a + b)([1, 2, 3, 4]) возвращает 10. |
group | group([5, 2, 2, 3, 3]) возвращает диапазон, содержащий кортежи tuple(5, 1), tuple(2, 2), и tuple(3, 2). |
joiner | joiner(["hello", "world!"], "; ") возвращает диапазон, который итерирует над символами "hello; world!". Новой строки не создаётся – итерирование ведётся по существующим входам. |
map | map!(a => a * 2)([1, 2, 3]) лениво возвращает диапазон с числами 2, 4, 6. |
permutations | Лениво вычисляет все перестановки, используя Heap's algorithm. |
reduce | reduce!((a, b) => a + b)([1, 2, 3, 4]) возвращает 10. Это старая реализация fold. |
splitter | Лениво делит диапазон по разделителю. |
sum | Тоже, что и fold, но специализируется на достоверном суммировании. |
uniq | Итерирует над уникальными элементами в диапазоне, который считается отсортированным. |
Исходный код: std/algorithm/iteration.d
cache
(Range)(Range range
)cacheBidirectional
(Range)(Range range
)cache
жадно считает front диапазона range
на каждой конструкции или вызове popFront,
чтобы сохранить результат в кеше. Затем, при вызове front, непосредственно возвращается результат, а не пересчитывается.
cache
может предоставить двунаправленную итерацию при необходимости, но так как это происходит при повышенной стоимости, её надо явно запросить через вызов cacheBidirectional
. Кроме того, двунаправленный кеш будет считать "центральный" элемент дважды, когда только один элемент останется в диапазоне.
cache
не предоставляет примитивы произвольного доступа, так как cache
не смог бы кэшировать случайный доступ. Если
Range предоставляет примитивы выделения среза, тогда cache
предоставит те же примитивы среза, но hasSlicing!Cache не вернёт true
(так как признак std.range.primitives.hasSlicing
также удостоверяется в наличии произвольного доступа).
Range range |
входной диапазон |
range
import std.algorithm.comparison : equal; import std.stdio, std.range; import std.typecons : tuple; ulong counter = 0; double fun(int x) { ++counter; // http://en.wikipedia.org/wiki/Quartic_function return ( (x + 4.0) * (x + 1.0) * (x - 1.0) * (x - 3.0) ) / 14.0 + 0.5; } // Без кеша, с массивом (жадным) auto result1 = iota(-4, 5).map!(a =>tuple(a, fun(a)))() .filter!(a => a[1] < 0)() .map!(a => a[0])() .array(); // значения x, при которых y отрицателен: assert(equal(result1, [-3, -2, 2])); // Проверяем, сколько раз вычислялась fun. // Столько же раз, сколько элементов в источнике и в результате вместе. assert(counter == iota(-4, 5).length + result1.length); counter = 0; // Без массива, с кэшем (лениво) auto result2 = iota(-4, 5).map!(a =>tuple(a, fun(a)))() .cache() .filter!(a => a[1] < 0)() .map!(a => a[0])(); // значения x, при которых y отрицателен: assert(equal(result2, [-3, -2, 2])); // Проверяем, сколько раз вычислялась fun. // Только столько, сколько элементов в источнике. assert(counter == iota(-4, 5).length);
cache
жаден при вычислении элементов. Если вызов front на лежащем в основе диапазоне имеет побочный эффект, то его можно будет наблюдать перед вызовом front во время текущего кэширования диапазона.
Кроме того, следует позаботиться о композиции cache
с std.range.take.
Если разместить take перед cache
, то cache
будет "знать", когда диапазон заканчивается, и корректно перестанет кешировать элементы, когда нужно. Если вызов front не имеет побочных эффектов, размещение take после cache
может дать более быстрый диапазон.
В любом случае, полученные диапазоны будут эквивалентны, но, возможно, не при тех же затратах или побочных эффектах.
import std.algorithm.comparison : equal; import std.range; int i = 0; auto r = iota(0, 4).tee!((a){i = a;}, No.pipeOnPop); auto r1 = r.take(3).cache(); auto r2 = r.cache().take(3); assert(equal(r1, [0, 1, 2])); assert(i == 2); //Последний "увиденный" элемент был 2. Данные в кеше очищены. assert(equal(r2, [0, 1, 2])); assert(i == 3); //cache получил доступ к 3. Он до сих пор хранятся внутри кэша.
map
(fun...) if (fun.length >= 1)map
(Range)(Range r) if (isInputRange!(Unqual!Range));
map
!(fun)(range)
возвращает диапазон, у которого элементы получены примением fun(a)
слева направо для всех элементов a в range. Исходные диапазоны не изменяются. Вычисление выполняется лениво.
fun | одна или больше функций |
Range r | входной диапазон |
import std.algorithm.comparison : equal; import std.range : chain; int[] arr1 = [ 1, 2, 3, 4 ]; int[] arr2 = [ 5, 6 ]; auto squares = map!(a => a * a)(chain(arr1, arr2)); assert(equal(squares, [ 1, 4, 9, 16, 25, 36 ]));
map
можно передать несколько функций. В этом случае, тип элемента будет кортежем, содержащим один элемент для каждой функции.
auto sums = [2, 4, 6, 8]; auto products = [1, 4, 9, 16]; size_t i = 0; foreach (result; [ 1, 2, 3, 4 ].map!("a + a", "a * a")) { assert(result[0] == sums[i]); assert(result[1] == products[i]); ++i; }
map
с некоторой функцией(ями) и использовать его отдельно:
import std.algorithm.comparison : equal; import std.conv : to; alias stringize = map!(to!string); assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ]));
each
(alias pred = "a")each
по умолчанию ничего не будет делать, но поглотит весь диапазон. .front будет рассчитываться, но этого можно избежать с помощью явного указания лямбда-предиката с ленивым параметром.
each
также поддерживает основанные на opApply итераторы, так что он будет работать, например, с std.parallelism.parallel.
pred | предикат, применяемый к каждому элементу диапазона |
Range r | диапазон или итерируемый объект, над которымeach итерирует |
import std.range : iota; long[] arr; iota(5).each!(n => arr ~= n); assert(arr == [0, 1, 2, 3, 4]); // Если диапазон это поддерживает, величину можно изменять на месте arr.each!((ref n) => n++); assert(arr == [1, 2, 3, 4, 5]); arr.each!"a++"; assert(arr == [2, 3, 4, 5, 6]); // ref-лямбды не поддерживаются для не-ref-диапазонов static assert(!is(typeof(arr.map!(n => n).each!((ref n) => n++)))); // предикат по-умолчанию поглощает диапазон auto m = arr.map!(n => n); (&m).each(); assert(m.empty); // Индексы также доступны для изменений на-месте arr[] = 0; arr.each!"a=i"(); assert(arr == [0, 1, 2, 3, 4]); // Итераторы opApply также работают static class S { int x; int opApply(scope int delegate(ref int _x) dg) { return dg(x); } } auto s = new S; s.each!"a++"; assert(s.x == 1);
filter
(alias predicate) if (is(typeof(unaryFun!predicate)))filter
(Range)(Range rs) if (isInputRange!(Unqual!Range));
predicate | Функция, применяемая к каждому элементу диапазона |
Range range | Входной диапазон элементов |
filter
!(predicate)(range) возвращает новый диапазон, содержащий только те элементы x в диапазоне range , для которых predicate(x) возвращает true
.
import std.algorithm.comparison : equal; import std.math : approxEqual; import std.range; int[] arr = [ 1, 2, 3, 4, 5 ]; auto small = filter!(a => a < 3)(arr); assert(equal(small, [ 1, 2 ])); // Тоже самое, через Uniform Function Call Syntax (UFCS) auto sum = arr.filter!(a => a < 3); assert(equal(sum, [ 1, 2 ])); // В сочетании с chain(), чтобы охватить несколько диапазонов int[] a = [ 3, -2, 400 ]; int[] b = [ 100, -101, 102 ]; auto r = chain(a, b).filter!(a => a > 0); assert(equal(r, [ 3, 400, 100, 102 ])); // Смешивание конвертируемых типов – тоже честная игра double[] c = [ 2.5, 3.0 ]; auto r1 = chain(c, a, b).filter!(a => cast(int) a != a); assert(approxEqual(r1, [ 2.5 ]));
filterBidirectional
(alias pred)filterBidirectional
(Range)(Range r) if (isBidirectionalRange!(Unqual!Range));
pred | Функция, применяемая к каждому элементу диапазона |
Range r | Двунаправленный диапазон элементов |
r
, для которых pred
возвращает true
.import std.algorithm.comparison : equal; import std.range; int[] arr = [ 1, 2, 3, 4, 5 ]; auto small = filterBidirectional!("a < 3")(arr); static assert(isBidirectionalRange!(typeof(small))); assert(small.back == 2); assert(equal(small, [ 1, 2 ])); assert(equal(retro(small), [ 2, 1 ])); // В комбинации chain(), чтобы распределять несколько диапазонов int[] a = [ 3, -2, 400 ]; int[] b = [ 100, -101, 102 ]; auto r = filterBidirectional!("a > 0")(chain(a, b)); assert(r.back == 102);
group
(alias pred = "a == b", Range)(Range r
);
Group
(alias pred, R) if (isInputRange!R);
group
формирует диапазон, который итерирует над уникальными последовательными элементами данного диапазона. Каждый элемент этого диапазона является кортежем из элемента и количества, сколько раз он повторялся в исходном диапазоне. Эквивалентность элементов определяется с использованием предиката pred, который установлен по умолчанию на "a == b". Предикат передаётся на std.functional.binaryFun,
и может принимать или строку, или любой вызываемый объект, который может быть выполнен через
pred(element, element).
pred | Двухаргументный предикат для определения эквивалентности двух элементов. |
Range r |
Входной диапазон для итерации. |
import std.algorithm.comparison : equal; import std.typecons : tuple, Tuple; int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ]; assert(equal(group(arr), [ tuple(1, 1u), tuple(2, 4u), tuple(3, 1u), tuple(4, 3u), tuple(5, 1u) ][]));
chunkBy
(alias pred, Range)(Range r
)chunkBy
, может вызвать исключение во время выполнения или работать нестабильно.
pred | Предикат для определения эквивалентности. |
Range r |
Диапазон, который будет разбит на части. |
Замечания: Эквивалентные элементы, разделённые промежуточным неэквивалентным элементом, будут появляться в отдельных поддиапазонах; эта функция принимает во внимание только прилегающую эквивалентность. Элементы в поддиапазонах всегда будут появляться в том же порядке, в каком они появляются в исходном диапазоне.
import std.algorithm.comparison : equal; // Группирование по конкретному атрибуту каждого элемента: auto data = [ [1, 1], [1, 2], [2, 2], [2, 3] ]; auto r1 = data.chunkBy!((a,b) => a[0] == b[0]); assert(r1.equal!equal([ [[1, 1], [1, 2]], [[2, 2], [2, 3]] ])); auto r2 = data.chunkBy!((a,b) => a[1] == b[1]); assert(r2.equal!equal([ [[1, 1]], [[1, 2], [2, 2]], [[2, 3]] ]));
import std.algorithm.comparison : equal; import std.typecons : tuple; import std.range.primitives; // Группирование по конкретному атрибуту каждого элемента: auto range = [ [1, 1], [1, 1], [1, 2], [2, 2], [2, 3], [2, 3], [3, 3] ]; auto byX = chunkBy!(a => a[0])(range); auto expected1 = [ tuple(1, [[1, 1], [1, 1], [1, 2]]), tuple(2, [[2, 2], [2, 3], [2, 3]]), tuple(3, [[3, 3]]) ]; foreach (e; byX) { assert(!expected1.empty); assert(e[0] == expected1.front[0]); assert(e[1].equal(expected1.front[1])); expected1.popFront(); } auto byY = chunkBy!(a => a[1])(range); auto expected2 = [ tuple(1, [[1, 1], [1, 1]]), tuple(2, [[1, 2], [2, 2]]), tuple(3, [[2, 3], [2, 3], [3, 3]]) ]; foreach (e; byY) { assert(!expected2.empty); assert(e[0] == expected2.front[0]); assert(e[1].equal(expected2.front[1])); expected2.popFront(); }
joiner
(RoR, Separator)(RoR r
, Separator sep
)joiner
(RoR)(RoR r
)sep
. Сам разделитель является диапазоном. Если разделитель не предусмотрен, тогда диапазоны соединяются непосредственно без чего-либо в между ними (в других языках часто называется flatten).
RoR r |
Входной диапазон входных диапазонов для соединения. |
Separator sep |
Лидирующий диапазон элемента(элементов), служащий в качестве разделителей в соединенном диапазоне. |
import std.algorithm.comparison : equal; import std.conv : text; assert(["abc", "def"].joiner.equal("abcdef")); assert(["Mary", "has", "a", "little", "lamb"] .joiner("...") .equal("Mary...has...a...little...lamb")); assert(["", "abc"].joiner("xyz").equal("xyzabc")); assert([""].joiner("xyz").equal("")); assert(["", ""].joiner("xyz").equal("xyz"));
reduce
!(fun)(seed, range) сначала присваивает seed (семя) внутренней переменной result, также называемой аккумулятором. Затем, для каждого элемента x в диапазоне вычисляется result = fun(result, x). Наконец, result возвращается.
Одноаргументная версия reduce
!(fun)(range)
работает аналогично, но использует первый элемент диапазона в качестве семени (дипазон должен быть не-пустым).
reduce
с обратным порядком аргументов, и без необходимости использовать кортеж для нескольких семян. Это делает её легче для использования в цепях UFCS.
sum аналогична reduce
!((a, b) => a + b) с парным суммированием чисел с плавающей запятой.reduce
.
Пример ниже иллюстрирует замечательную мощность и гибкость reduce
.
import std.algorithm.comparison : max, min; import std.math : approxEqual; import std.range; int[] arr = [ 1, 2, 3, 4, 5 ]; // Сумма всех элементов auto sum = reduce!((a,b) => a + b)(0, arr); assert(sum == 15); // Снова сумма, с использованием строкового предиката с "a" и "b" sum = reduce!"a + b"(0, arr); assert(sum == 15); // Вычислить максимум всех элементов auto largest = reduce!(max)(arr); assert(largest == 5); // Снова максимум, но с использованием Uniform Function Call Syntax (UFCS) largest = arr.reduce!(max); assert(largest == 5); // Вычислить количество нечетных элементов auto odds = reduce!((a,b) => a + (b & 1))(0, arr); assert(odds == 3); // Вычислить сумму квадратов auto ssquares = reduce!((a,b) => a + b * b)(0, arr); assert(ssquares == 55); // Цепь нескольких диапазонов int[] a = [ 3, 4 ]; int[] b = [ 100 ]; auto r = reduce!("a + b")(chain(a, b)); assert(r == 107); // Смешивание конвертируемых типов – тоже честная игра double[] c = [ 2.5, 3.0 ]; auto r1 = reduce!("a + b")(chain(a, b, c)); assert(approxEqual(r1, 112.5)); // Для минимизации вложенности скобок можно использовать Uniform Function Call Syntax auto r2 = chain(a, b, c).reduce!("a + b"); assert(approxEqual(r2, 112.5));
reduce
принимает несколько функций. Если переданы две или больше функций, reduce
возвращает объект
std.typecons.Tuple (кортеж), в котором по одному члену на каждую переданную функцию. Количество семян должно соответственно возрасти.
import std.algorithm.comparison : max, min; import std.math : approxEqual, sqrt; import std.typecons : tuple, Tuple; double[] a = [ 3.0, 4, 7, 11, 3, 2, 5 ]; // Вычислить минимум и максимум за один проход auto r = reduce!(min, max)(a); // Тип r - это Tuple!(int, int) assert(approxEqual(r[0], 2)); // минимум assert(approxEqual(r[1], 11)); // максимум // Вычислить сумму и сумму квадратов за один проход r = reduce!("a + b", "a + b * b")(tuple(0.0, 0.0), a); assert(approxEqual(r[0], 35)); // сумма assert(approxEqual(r[1], 233)); // сумма квадратов // Вычислить среднее и среднеквадратичное отклонение из предыдущего auto avg = r[0] / a.length; auto stdev = sqrt(r[1] / a.length - avg * avg);
r
используется как величина seed.
r
: ElementType!R для диапазонов, и ForeachType!R в противном случае.
Как только S определён, затем S s = e; и s = f(s, e);
оба должны быть легальными.
Если r
пуст, тогда бросается исключение.
fun | одна или более функций |
R r |
итерируемая величина, как определено isIterable |
reduce
(S, R)(S seed
, R r
)seed
должно быть единственным значением, если fun является единственной функцией. Если fun – это несколько функций, тогда seed
должно быть std.typecons.Tuple, с одним полем на функцию в f.
seed
– константа или квалифицированное поле, тогда
reduce
будет действовать на неквалифицированной копии. Если это случится, тогда возвращаемый тип не вполне будет соответствовать S.
Используйте fold вместо reduce
, чтобы использовать версию с семенем в цепи UFCS.
fun | одна или более функций |
S seed |
начальное значение аккумулятора |
R r |
итерируемая величина, как определено isIterable |
fold
(fun...) if (fun.length >= 1)fold
!(fun)(range, seed)
сначала присваивает seed (семя) внутренней переменной result, также называемую аккумулятором. Затем, для каждого элемента x в диапазоне вычисляется result = fun(result, x). Наконец, result возвращается. Одноаргументная версия fold
!(fun)(range)
работает аналогично, но использует первый элемент диапазона как семя (диапазон должен быть не-пустым).fold
!((a, b) => a + b), что обеспечивает точное суммирование чисел с плавающей запятой.
Это функционально эквивалентно reduce с обратным порядком аргументов, и без необходимости использовать кортеж для нескольких семян.immutable arr = [1, 2, 3, 4, 5]; // Сумма всех элементов assert(arr.fold!((a, b) => a + b) == 15); // Просуммировать все элементы с явным семенем assert(arr.fold!((a, b) => a + b)(6) == 21); import std.algorithm.comparison : min, max; import std.typecons : tuple; // Вычислить минимум и максимум одновременно assert(arr.fold!(min, max) == tuple(1, 5)); // Вычислить минимум и максимум одновременно с семенем assert(arr.fold!(min, max)(0, 7) == tuple(0, 7)); // Можно использовать в цепи UFCS assert(arr.map!(a => a + 1).fold!((a, b) => a + b) == 20);
cumulativeFold
!(fun)(range, seed) сначала присваивает seed внутренней переменной result, также называемой аккумулятором. Возвращаемый диапазон содержит значения result = fun(result, x), лениво вычисляемые для каждого элемента x в диапазоне. Наконец, последний элемент имеет то же значение, что и fold!(fun)(seed, range).
Одноаргументная версия cumulativeFold
!(fun)(range) работает аналогично, но здесь первый элемент возвращается неизменным и используется как семя для следующих элементов. Эта функция также известна как
partial_sum,
accumulate,
scan,
Cumulative Sum.
import std.algorithm.comparison : max, min; import std.array : array; import std.math : approxEqual; import std.range : chain; int[] arr = [1, 2, 3, 4, 5]; // Частичная сумма всех элементов auto sum = cumulativeFold!((a, b) => a + b)(arr, 0); assert(sum.array == [1, 3, 6, 10, 15]); // Снова частичная сумма, с использованием строкового предиката с "a" и "b" auto sum2 = cumulativeFold!"a + b"(arr, 0); assert(sum2.array == [1, 3, 6, 10, 15]); // Вычислить частичный максимум всех элементов auto largest = cumulativeFold!max(arr); assert(largest.array == [1, 2, 3, 4, 5]); // Снова частичный максимум, но с использованием Uniform Function Call Syntax (UFCS) largest = arr.cumulativeFold!max; assert(largest.array == [1, 2, 3, 4, 5]); // Частичное количество нечетных элементов auto odds = arr.cumulativeFold!((a, b) => a + (b & 1))(0); assert(odds.array == [1, 1, 2, 2, 3]); // Вычислить частичную сумму квадратов auto ssquares = arr.cumulativeFold!((a, b) => a + b * b)(0); assert(ssquares.array == [1, 5, 14, 30, 55]); // Цепь нескольких диапазонов int[] a = [3, 4]; int[] b = [100]; auto r = cumulativeFold!"a + b"(chain(a, b)); assert(r.array == [3, 7, 107]); // Смешивание конвертируемых типов – тоже честная игра double[] c = [2.5, 3.0]; auto r1 = cumulativeFold!"a + b"(chain(a, b, c)); assert(approxEqual(r1, [3, 7, 107, 109.5, 112.5])); // Для минимизации вложенности скобок можно использовать Uniform Function Call Syntax auto r2 = chain(a, b, c).cumulativeFold!"a + b"; assert(approxEqual(r2, [3, 7, 107, 109.5, 112.5]));
cumulativeFold
принимает несколько функций. Если переданы две или больше функций, cumulativeFold
возвращает объект std.typecons.Tuple (кортеж), в котором по одному члену на каждую переданную функцию. Количество семян должно соответственно возрасти.
import std.algorithm.comparison : max, min; import std.algorithm.iteration : map; import std.math : approxEqual; import std.typecons : tuple; double[] a = [3.0, 4, 7, 11, 3, 2, 5]; // Вычислить минимум и максимум за один проход auto r = a.cumulativeFold!(min, max); // Тип r - это Tuple!(int, int) assert(approxEqual(r.map!"a[0]", [3, 3, 3, 3, 3, 2, 2])); // минимум assert(approxEqual(r.map!"a[1]", [3, 4, 7, 11, 11, 11, 11])); // максимум // Вычислить сумму и сумму квадратов за один проход auto r2 = a.cumulativeFold!("a + b", "a + b * b")(tuple(0.0, 0.0)); assert(approxEqual(r2.map!"a[0]", [3, 7, 14, 25, 28, 30, 35])); // сумма assert(approxEqual(r2.map!"a[1]", [9, 25, 74, 195, 204, 208, 233])); // сумма квадратов
fun | одна или больше функций |
R range |
Входной диапазон, как определено isInputRange |
cumulativeFold
(R, S)(R range
, S seed
)seed
должно быть единственным значением, если fun является единственной функцией. Если fun – это несколько функций, тогда seed
должно быть
std.typecons.Tuple, с одним полем на функцию в f.
Для удобства, если seed
– константа или квалифицированное поле, тогда
cumulativeFold
будет действовать на неквалифицированной копии. Если это случится, тогда возвращаемый тип не вполне будет соответствовать S.
fun | одна или несколько функций |
R range |
входной диапазон, как определено isInputRange |
S seed |
начальное значение аккумулятора |
s
).
Если дан пустой диапазон, результатом будет диапазон с одним пустым элементом. Если дан диапазон с одним разделителем, результатом будет диапазон с двумя пустыми элементами.
Если строка разбивается по пробельным символам и требуется сжать токены, рекомендуется использовать splitter
без указания разделителя (см. четвертую перегрузку ниже).
pred | Предикат для сравнения каждого элемента с разделителем, по умолчанию "a == b". |
Range r |
Входной диапазон для разделения. Должен поддерживать срезы и свойство .length. |
Separator s |
Элемент, который будет рассматриваться как разделитель между сегментами диапазона для разделения. |
Ограничения:
Предикат pred должен принимать элемент r
и разделитель s
.
r
– лидирующий диапазон или двунаправленный диапазон, возвращаемый диапазон будет таким же.
import std.algorithm.comparison : equal; assert(equal(splitter("hello world", ' '), [ "hello", "", "world" ])); int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ]; int[][] w = [ [1, 2], [], [3], [4, 5], [] ]; assert(equal(splitter(a, 0), w)); a = [ 0 ]; assert(equal(splitter(a, 0), [ (int[]).init, (int[]).init ])); a = [ 0, 1 ]; assert(equal(splitter(a, 0), [ [], [1] ])); w = [ [0], [1], [2] ]; assert(equal(splitter!"a.front == b"(w, 1), [ [[0]], [[2]] ]));
splitter
(alias pred = "a == b", Range, Separator)(Range r
, Separator s
)r
.front, s
.front))
: bool) && (hasSlicing!Range || isNarrowString!Range)
&& isForwardRange!Separator && (hasLength!Separator ||
isNarrowString!Separator));
splitter
, за исключением того, что использует другой диапазон как разделитель. Он может использоваться любым типом узкой строки или может типом диапазона с поддержкой срезов, но популярнее всего со строковыми типами. Предикат передаётся в
std.functional.binaryFun, и может принимать как строку, так и любой вызываемый объект, который может выполнять pred(r
.front, s
.front).
pred | Предикат для сравнения каждого элемента с разделителем, по умолчанию "a == b". |
Range r |
Входной диапазон для разделения. |
Separator s |
Лидирующий диапазон , рассматриваемый как разделитель между сегментами r . |
Ограничения:
Предикат pred должен принимать элемент r
и разделитель s
.
r
– лидирующий диапазон или двунаправленный диапазон, возвращаемый диапазон будет таким же.
import std.algorithm.comparison : equal; assert(equal(splitter("hello world", " "), [ "hello", "world" ])); int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ]; int[][] w = [ [1, 2], [3, 0, 4, 5, 0] ]; assert(equal(splitter(a, [0, 0]), w)); a = [ 0, 0 ]; assert(equal(splitter(a, [0, 0]), [ (int[]).init, (int[]).init ])); a = [ 0, 0, 1 ]; assert(equal(splitter(a, [0, 0]), [ [], [1] ]));
splitter
(alias isTerminator, Range)(Range input
)input
.front))));
splitter
, за исключением того, что не использует разделитель. Вместо этого, предикат является одноаргументной функцией над типом элементов входного диапазона. Предикат isTerminator передаётся на std.functional.unaryFun и может принимать как строку, так и любой вызываемый объект, который может выполнять pred(element).
isTerminator | Предикат для принятия решения, где разделить диапазон. |
Range input |
Входной диапазон для разделения. |
Ограничения:
Предикат isTerminator должен принимать элемент input
.
r
– лидирующий диапазон или двунаправленный диапазон, возвращаемый диапазон будет таким же.
import std.algorithm.comparison : equal; assert(equal(splitter!(a => a == ' ')("hello world"), [ "hello", "", "world" ])); int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ]; int[][] w = [ [1, 2], [], [3], [4, 5], [] ]; assert(equal(splitter!(a => a == 0)(a), w)); a = [ 0 ]; assert(equal(splitter!(a => a == 0)(a), [ (int[]).init, (int[]).init ])); a = [ 0, 1 ]; assert(equal(splitter!(a => a == 0)(a), [ [], [1] ])); w = [ [0], [1], [2] ]; assert(equal(splitter!(a => a.front == 1)(w), [ [[0]], [[2]] ]));
splitter
(C)(C[] s
)s
на слова, используя пробел как разделитель.
splitter
!(std.uni.isWhite), несколько пробелов подряд объединяются вместе (никакие пустые токены не будут производиться).
C[] s |
Строка для разделения. |
import std.algorithm.comparison : equal; auto a = " a bcd ef gh "; assert(equal(splitter(a), ["a", "bcd", "ef", "gh"][]));
sum
(R)(R r
)r
.front + r
.front)));
sum
(R, E)(R r
, E seed
)seed
= seed
+ r
.front)));
r
, который должен быть конечным
входным диапазоном. Хотя в принципе sum
(r
) эквивалентна fold!((a, b) => a +
b)(r
, 0), sum
использует специализированные алгоритмы, максимузирующие точность, следующим образом.
sum
использует
pairwise summation
алгоритм.sum
использует алгоритм Кэхэна.sum
можно передать семя seed
. Это семя не только будет использоваться как начальное значение, но и его тип аннулирует всё, сказанное выше, и определяет алгоритм и точность, используемые для суммирования.
Заметьте, что эти специализированные алгоритмы суммирования выполняют больше простых операций, чем обычное суммирование. Поэтому, если в определённых случаях максимальная скорость требуется за счет точности, можно использовать fold!((a, b) => a + b)(r
, 0), который не специализируется на суммировании.
E seed |
начальное значение для суммирования |
R r |
конечный входной диапазон |
r
.uniq
(alias pred = "a == b", Range)(Range r
)r
.front, r
.front)) == bool));
uniq
также производит двунаправленный диапазон.
pred | Предикат для определения эквивалентности между элементами диапазона. |
Range r |
Входной диапазон элементов для фильтрации. |
r
также является лидирующим диапазоном или двунаправленным диапазоном, возвращаемый диапазон будет таким же.import std.algorithm.mutation : copy; import std.algorithm.comparison : equal; int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ]; assert(equal(uniq(arr), [ 1, 2, 3, 4, 5 ][])); // Фильтрация дубликатов на-месте, используя копию arr.length -= arr.uniq().copy(arr).length; assert(arr == [ 1, 2, 3, 4, 5 ]); // Заметьте, что уникальность определяется только для последовательных элементов; // дублированные элементы, разделенные промежуточным отличающимся элементом, // не будут устраняться: assert(equal(uniq([ 1, 1, 2, 1, 1, 3, 1]), [1, 2, 1, 3, 1]));
permutations
(Range)(Range r
)Permutations
(Range) if (isRandomAccessRange!Range && hasLength!Range);
r
, используя Heap's algorithm.
r
.
import std.algorithm.comparison : equal; import std.range : iota; assert(equal!equal(iota(3).permutations, [[0, 1, 2], [1, 0, 2], [2, 0, 1], [0, 2, 1], [1, 2, 0], [2, 1, 0]]));