Данная реализация очень похожа на std::accumulate
, если считать параметр out
переменной init
и каким-то образом заменить функцией f
конструкцию if-construct
и ее тело!
Мы на самом деле сделали это — создали данную конструкцию if-construct
и ее тело с помощью объекта бинарной функции, предоставленного в качестве параметра функции std::accumulate
:
auto copy_and_advance ([](auto it, auto input) {
*it = input;
return ++it;
});
Функция std::accumulate
помещает переменную init
в параметр бинарной функции it
. Второй параметр — текущее значение из диапазона, получаемое в цикле. Мы предоставили init
функции std::accumulate
. Таким образом, функция std::accumulate
не считает сумму, а перемещает элементы, по которым итерирует, в другой диапазон. Это значит следующее: мы повторно реализовали функцию std::copy
, которая пока что не имеет предикатов и преобразований.
Фильтрацию с помощью предиката добавим путем обертывания функции copy_and_advance
в
template
auto filter(T predicate)
{
return [=] (auto reduce_fn) {
return [=] (auto accum, auto input) {
if (predicate(input)) {
return reduce_fn(accum, input);
} else {
return accum;
}
};
};
}
Эта конструкция на первый взгляд выглядит сложной, но посмотрим на конструкцию if
. Если функция-предикат вернет значение true
, то параметры будут перенаправлены функции reduce_fn
, в роли которой в нашем случае выступает функция copy_and_advance
. Если же предикат вернет значение false
, то переменная accum
, выступающая в роли переменной init
функции std::accumulate
, будет возвращена без изменений. Так мы реализуем ту часть операции фильтрации, где if
находится внутри лямбда-выражения, которое имеет такую же сигнатуру бинарной функции, что и функция copy_and_advance;
это делает ее хорошей заменой.
Теперь мы можем map
:
template
auto map(T fn)
{
return [=] (auto reduce_fn) {
return [=] (auto accum, auto input) {
return reduce_fn(accum, fn(input));
};
};
}
Теперь код выглядит гораздо проще. Он тоже содержит внутреннее лямбда-выражение, которое имеет такую же сигнатуру, как и функция copy_and_advance
, поэтому способен заменить ее. Реализация просто направляет дальше входные значения, но притом fn
.
Далее, когда мы воспользуемся этими вспомогательными функциями, напишем следующее выражение:
filter(even)(
map(twice)(
copy_and_advance
)
)
Вызов filter(even)
захватывает предикат even
и дает функцию, которая принимает бинарную функцию, чтобы обернуть ее в другую бинарную функцию, выполняющую map(twice)
делает то же самое с функцией преобразования twice
, но оборачивает бинарную функцию copy_and_advance
в другую бинарную функцию, всегда преобразующую правый параметр.
Не выполнив оптимизацию, мы получим сложнейшую конструкцию, состоящую из вложенных функций, которые вызывают другие функции и при этом выполняют совсем немного работы. Однако компилятор может легко оптимизировать подобный код. Полученная бинарная функция так же проста, как и результат более прямолинейной реализации функции transform_if
. Мы ничего не теряем с точки зрения производительности, приобретая компонуемость, свойственную функциям, поскольку смогли объединить предикат even
и функцию преобразования twice
столь же легко, как если бы на их месте были детали «Лего».
Генерируем декартово произведение на основе любых входных данных во время компиляции
Лямбда-выражения и наборы параметров можно использовать для решения сложных задач. В этом разделе мы реализуем объект функции, который принимает произвольное количество входных параметров и генерирует