std.functional

Переместиться к: adjoin · binaryFun · binaryReverseArgs · compose · equalTo · forward · greaterThan · lessThan · memoize · not · partial · pipe · reverseArgs · toDelegate · unaryFun

Функции, которые манипулируют другими функциями.
Этот модуль предоставляет функции для композиции функций времени компиляции. Эти функции полезны при создании предикатов для алгоритмов в std.algorithm или std.range.
Function Name Description
adjoin Объединяет несколько функций в одну, которая выполняет первоначальные функции независимо друг от друга, и возвращает кортеж со всеми результатами.
compose, pipe Соединяет несколько функций в одну, которая выполняет первоначальные функции, одну за другой, используя результат одной функции как аргумент в следующей функции.
forward Передаёт аргументы функции при сохранении ref-ности.
lessThan, greaterThan, equalTo Готовые функции-предикаты для сравнения двух величин.
memoize Создает функцию, которая кеширует свой результат для быстрого повторного вычисления.
not Создает функцию, которая отрицает другую.
partial Создает функцию, в которой первый аргумент данной функции привязан к данной величине.
reverseArgs, binaryReverseArgs Предикат, который инвертирует порядок своих аргументов.
toDelegate Преобразует вызываемый объект в делегат.
unaryFun, binaryFun Создаёт одноаргументную или двухаргументную функцию из строки. Чаще всего используется при определении алгоритмов на диапазонах.
Лицензия:
Boost License 1.0.
Авторы:
Andrei Alexandrescu

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

template unaryFun(alias fun, string parmName = "a")
Преобразует строку, представляющую выражение, в одноаргументную функцию. Строка должна или использовать символ a для имени параметра, или предоставить идентификатор параметра через аргумент parmName. Если fun — это не строка, псевдонимы unaryFun сами приводятся к fun.
Примеры:
// Строки компилируются в функции:
alias isEven = unaryFun!("(a & 1) == 0");
assert(isEven(2) && !isEven(1));
template binaryFun(alias fun, string parm1Name = "a", string parm2Name = "b")
Преобразует строку, представляющую выражение, в двухаргументную функцию. Строка должна или использовать символы a и b для имён параметров или предоставить идентификаторы через аргументы parm1Name и parm2Name. Если fun — это не строка, псевдонимы binaryFun сами приводятся к fun.
Примеры:
alias less = binaryFun!("a < b");
assert(less(1, 2) && !less(2, 1));
alias greater = binaryFun!("a > b");
assert(!greater("1", "2") && greater("2", "1"));
alias lessThan = safeOp!"<".safeOp(T0, T1)(auto ref T0 a, auto ref T1 b);
Предикат, который возвращает a < b. Правильно сравнивает знаковые и беззнаковые целые, т.е. -1 < 2U.
Примеры:
assert(lessThan(2, 3));
assert(lessThan(2U, 3U));
assert(lessThan(2, 3.0));
assert(lessThan(-2, 3U));
assert(lessThan(2, 3U));
assert(!lessThan(3U, -2));
assert(!lessThan(3U, 2));
assert(!lessThan(0, 0));
assert(!lessThan(0U, 0));
assert(!lessThan(0, 0U));
alias greaterThan = safeOp!">".safeOp(T0, T1)(auto ref T0 a, auto ref T1 b);
Предикат, который возвращает a > b. Правильно сравнивает знаковые и беззнаковые целые, т.е. 2U > -1.
Примеры:
assert(!greaterThan(2, 3));
assert(!greaterThan(2U, 3U));
assert(!greaterThan(2, 3.0));
assert(!greaterThan(-2, 3U));
assert(!greaterThan(2, 3U));
assert(greaterThan(3U, -2));
assert(greaterThan(3U, 2));
assert(!greaterThan(0, 0));
assert(!greaterThan(0U, 0));
assert(!greaterThan(0, 0U));
alias equalTo = safeOp!"==".safeOp(T0, T1)(auto ref T0 a, auto ref T1 b);
Предикат, который возвращает a == b. Правильно сравнивает знаковые и беззнаковые целые, т.е. !(-1 == ~0U).
Примеры:
assert(equalTo(0U, 0));
assert(equalTo(0, 0U));
assert(!equalTo(-1, ~0U));
template reverseArgs(alias pred)
N-аргументный предикат, который обращает порядок аргументов, т.е., дано pred(a, b, c), возвращает pred(c, b, a).
Примеры:
alias gt = reverseArgs!(binaryFun!("a < b"));
assert(gt(2, 1) && !gt(1, 1));
int x = 42;
bool xyz(int a, int b) { return a * x < b / x; }
auto foo = &xyz;
foo(4, 5);
alias zyx = reverseArgs!(foo);
assert(zyx(5, 4) == foo(4, 5));
Примеры:
int abc(int a, int b, int c) { return a * b + c; }
alias cba = reverseArgs!abc;
assert(abc(91, 17, 32) == cba(32, 17, 91));
Примеры:
int a(int a) { return a * 2; }
alias _a = reverseArgs!a;
assert(a(2) == _a(2));
Примеры:
int b() { return 4; }
alias _b = reverseArgs!b;
assert(b() == _b());
template binaryReverseArgs(alias pred)
Двухаргументный предикат, который обращает порядок аргументов, т.е., дано pred(a, b), возвращает pred(b, a).
Примеры:
alias gt = binaryReverseArgs!(binaryFun!("a < b"));
assert(gt(2, 1) && !gt(1, 1));
Примеры:
int x = 42;
bool xyz(int a, int b) { return a * x < b / x; }
auto foo = &xyz;
foo(4, 5);
alias zyx = binaryReverseArgs!(foo);
assert(zyx(5, 4) == foo(4, 5));
template not(alias pred)
Отрицает предикат pred.
Примеры:
import std.algorithm.searching : find;
import std.functional;
import std.uni : isWhite;
string a = "   Hello, world!";
assert(find!(not!isWhite)(a) == "Hello, world!");
template partial(alias fun, alias arg)
Частично применяет fun, связывая первый аргумент с arg.
Примеры:
int fun(int a, int b) { return a + b; }
alias fun5 = partial!(fun, 5);
assert(fun5(6) == 11);
// Заметьте, что в большинстве случаев вы будете использовать псевдоним
// вместо назначаемой величины. Использование псевдонима позволяет вам 
// частично вычислить шаблон функции без привязки к конкретному типу функции.
template adjoin(F...) if (F.length == 1)

template adjoin(F...) if (F.length > 1)
Принимает множество функций и сопрягает их между собой. Результатом является кортеж std.typecons.Tuple с одним элементом на каждую переданную функцию. При вызове возвращается кортеж с результатами всех функций.

Примечание: В специальном случае, когда предоставлена только одна единственная функция (F.length == 1), adjoin просто возвращает псевдоним к переданной единичной функции (F[0]).

Примеры:
import std.functional, std.typecons : Tuple;
static bool f1(int a) { return a != 0; }
static int f2(int a) { return a / 2; }
auto x = adjoin!(f1, f2)(5);
assert(is(typeof(x) == Tuple!(bool, int)));
assert(x[0] == true && x[1] == 2);
template compose(fun...)
Компонует переданные функции fun[0], fun[1], ..., возвращая функцию f(x), которая, в свою очередь, возвращает fun[0](fun[1](...(x))).... Каждая из функций может быть регулярной функцией, делегатом, или строкой.
Примеры:
import std.algorithm.comparison : equal;
import std.algorithm.iteration : map;
import std.array : split;
import std.conv : to;

// Сначала разбить строку на разделенные пробелами лексемы,
// а затем преобразовать каждую лексему в целое число
assert(compose!(map!(to!(int)), split)("1 2 3").equal([1, 2, 3]));
template pipe(fun...)
Подаёт (Pipes — буквально "подавать по трубам" — прим. пер.) функции в последовательность. Предлагает ту же функциональность, что и compose, но с функциями, заданными в обратном порядке. Это может привести к более читаемому коду в какой-то ситуации, потому что порядок исполнения такой же, как в лексическом порядке.

Пример:

// Прочитать весь текстовый файл, разделить результирующую строку
// на разделённые пробелами лексемы, затем преобразовать 
// каждую лексему в целое
int[] a = pipe!(readText, split, map!(to!(int)))("file.txt");

ReturnType!fun memoize(alias fun)(Parameters!fun args);

ReturnType!fun memoize(alias fun, uint maxSize)(Parameters!fun args);
Мемоизирует функцию, чтобы избегать повторения вычислений. Структура мемоизации является хэш-таблицей с ключом в виде кортежа аргументов функции. Существует прирост скорости, если функция неоднократно вызывается с одними и теми же аргументами, и она более дорогая, чем поиск в хэш-таблице. Для получения дополнительной информации о мемоизации, обратитесь к этой книге.

Пример:

double transmogrify(int a, string b)
{
   ... дорогое вычисление ...
}
alias fastTransmogrify = memoize!transmogrify;
unittest
{
    auto slow = transmogrify(2, "hello");
    auto fast = fastTransmogrify(2, "hello");
    assert(slow == fast);
}
Технически мемоизируемая функция должна быть чистой (pure), поскольку memoize предполагает, что она всегда возвращает одинаковый результат для данного кортежа аргументов. Тем не менее, memoize не навязывает это, поскольку иногда бывает полезно мемоизировать также нечистую функцию.

Примеры:
Чтобы мемоизировать рекурсивную функцию, просто вставьте мемоизированный вызов вместо простого рекурсивного вызова. Например, чтобы преобразовать реализацию функции Fibonacci с экспоненциальным временем к линейному времени вычисления:
ulong fib(ulong n) @safe
{
    return n < 2 ? n : memoize!fib(n - 2) + memoize!fib(n - 1);
}
assert(fib(10) == 55);
Примеры:
Чтобы улучшить скорость функции факториала,
ulong fact(ulong n) @safe
{
    return n < 2 ? 1 : n * memoize!fact(n - 1);
}
assert(fact(10) == 3628800);
Примеры:
Это мемоизирует все значения fact вплоть до самого большого аргумента. Для того, чтобы кешировать только конечный результат, переместите memoize за пределы функции, как показано ниже.
ulong factImpl(ulong n) @safe
{
    return n < 2 ? 1 : n * factImpl(n - 1);
}
alias fact = memoize!factImpl;
assert(fact(10) == 3628800);
Примеры:
Когда задан параметр maxSize, memoize будет использовать хэш-таблицу фиксированного размера, чтобы ограничить количество кешируемых данных.
ulong fact(ulong n)
{
    // Мемоизировать не больше 8 значений
    return n < 2 ? 1 : n * memoize!(fact, 8)(n - 1);
}
assert(fact(8) == 40320);
// использование большего количества данных, чем maxSize, перезапишет существующие данные
assert(fact(10) == 3628800);
auto toDelegate(F)(auto ref F fp)
if (isCallable!F);
Конвертирует вызываемый объект в делегат с тем же списком параметров и возвращаемым типом, избегая распределений кучи и использования вспомогательной памяти.

Пример:

void doStuff() {
    writeln("Hello, world.");
}

void runDelegate(void delegate() myDelegate) {
    myDelegate();
}

auto delegateToPass = toDelegate(&doStuff);
runDelegate(delegateToPass);  // Вызывает doStuff, печатает "Hello, world."

Недостатки:
  • Не работает с @safe-функциями.
  • Игнорирует вариативные аргументы (variadic) в C-стиле / D-стиле.
template forward(args...)
Передаёт аргументы функции с сохранением ref-ности.
Примеры:
class C
{
    static int foo(int n) { return 1; }
    static int foo(ref int n) { return 2; }
}
int bar()(auto ref int x) { return C.foo(forward!x); }

assert(bar(1) == 1);
int i;
assert(bar(i) == 2);
Примеры:
void foo(int n, ref string s) { s = null; foreach (i; 0..n) s ~= "Hello"; }

// передает все аргументы, которые связаны с кортежем параметров 
void bar(Args...)(auto ref Args args) { return foo(forward!args); }

// передает все аргументы с изменением порядка 
void baz(Args...)(auto ref Args args) { return foo(forward!args[$/2..$], forward!args[0..$/2]); }

string s;
bar(1, s);
assert(s == "Hello");
baz(s, 2);
assert(s == "HelloHello");