if (predicate(input)) {
return reduce_fn(accum, input);
} else {
return accum;
}
};
};
}
6. Теперь воспользуемся этими вспомогательными функциями. Создадим экземпляры итераторов, которые позволяют считать целочисленные значения из стандартного потока ввода:
int main()
{
std::istream_iterator
std::istream_iterator
7. Далее определим функцию-предикат even
, которая возвращает значение true
, если перед нами twice
умножает свой целочисленный параметр на 2
:
auto even ([](int i) { return i % 2 == 0; });
auto twice ([](int i) { return i * 2; });
8. Функция std::accumulate
принимает диапазон значений и +
. Мы хотим предоставить собственную функцию аккумулирования. Таким образом, хранить it
, а затем вернем его после
auto copy_and_advance ([](auto it, auto input) {
*it = input; return ++it;
});
9. Наконец мы готовы собрать все воедино. Мы итерируем по стандартному потоку ввода и предоставляем вывод ostream_iterator
, который выдает значения в консоль. Объект функции copy_and_advance
работает с этим итератором вывода, присваивая ему целые числа, полученные от пользователя. По сути, данное действие выводит на экран присвоенные элементы. Но мы хотим видеть только четные числа, полученные от пользователя, и умножить их. Для этого оборачиваем функцию copy_and_advance
в twice
.
std::accumulate(it, end_it,
std::ostream_iterator
filter(even)(
map(twice)(
copy_and_advance
)
));
std::cout << '\n';
}
10. Компиляция и запуск программы дадут следующий результат. Значения 1
, 3
и 5
отбрасываются, поскольку являются нечетными, а 2
, 4
и 6
выводятся на экран после их умножения на два.
$ echo "1 2 3 4 5 6" | ./transform_if
4, 8, 12,
Как это работает
Этот пример выглядит довольно сложным, поскольку мы активно использовали вложенные лямбда-выражения. Чтобы понять, как все работает, взглянем на внутреннее содержимое функции std::accumulate
. Именно так она выглядит в обычной реализации, предлагаемой в библиотеке STL:
template
T accumulate(InputIterator first, InputIterator last, T init, F f)
{
for (; first != last; ++first) {
init = f(init, *first);
}
return init;
}
Параметр функции f
выполняет здесь остальную работу, а цикл собирает результаты в предоставленной пользователем переменной init
. В обычном варианте использования диапазон итераторов может представлять собой вектор чисел, например 0
, 1
, 2
, 3
, 4
, а переменная init
будет иметь значение 0
. Функция f
является простой бинарной функцией, которая может определять +
.
В нашем примере цикл просто складывает все элементы и записывает результат в переменную init
, это выглядит, например, так: init = (((0+1)+2)+3)+4
. Подобная запись помогает понять, что std::accumulate
представляет собой функцию std::transform_if
! Функция f
в таком случае будет называться функцией
Очень прямолинейная реализация функции transform_if
будет выглядеть так:
template
typename P, typename Transform>
OutputIterator transform_if(InputIterator first, InputIterator last,
OutputIterator out,
P predicate, Transform trans)
{
for (; first != last; ++first) {
if (predicate(*first)) {
*out = trans(*first);
++out;
}
}
return out;
}