Странное поведение std::reduce
#include <iostream>
#include <string_view>
#include <numeric>
using namespace std;
int CountWords(string_view str) {
auto op = [](int accum, char c) -> int {
return accum + ((c == ' ') ? 1 : 0);
};
return std::reduce(str.begin(), str.end(), (int)0, op);
}
int main() {
// должно вывести 6
cout << CountWords("pretty little octopus dnskdj kjanfkj snfs jknds"sv) << endl;
return 0;
}
Объясните пожалуйста, что не так с этим кодом? Почему он выводит 2 вместо 6? Код должен посчитать пробелы в строке
Ответы (1 шт):
Вы перепутали алгоритм. То, что вам нужно ::std::accumulate, a ::std::reduce - это обобщенная сумма. При его выполнении первый элемент в функторе может быть значением символа из входящей строки, а не накопленное значение.
Обратите внимание на требования алгоритма:
T must meet the requirements of MoveConstructible. and
binary_op(init, *first),binary_op(*first, init),binary_op(init, init), andbinary_op(*first, *first)must be convertible toT.
В этом легко убедиться, напечатав значения, поступающие в функтор:
auto op = [](int accum, char c) -> int {
::std::cout << accum << " \"" << c << "\"\n";
return accum + ((c == ' ') ? 1 : 0);
};
112 "r"
101 "t"
112 "e"
0 "p"
116 "y"
32 "l"
116 " "
0 "u"
105 "t"
116 "l"
105 "t"
...
При вызове accumulate происходит то, что вы предполагаете - для каждого элемента вызывается функтор в котором первым аргументом идет накопитель, а вторым - символ из входящей строки. При вызове reduce в функтор и первым, и вторым аргументом могут подавать и накопитель и символы. reduce может складывать элементы последовательности между собой (а может и не складывать).
Также важное отличие заключается в требовании ко входящим итераторам - accumulate работает с input iterator и поэтому работает всегда последовательно и не поддерживает параллелизм, а reduce имеет перегрузку с forward iterator и может быть выполнен параллельно.
Пример для суммирования массива:
accumulate
4 9 8 5 2 1 0 6
13 ┘ │ │ │ │ │
21 ┘ │ │ │ │
26 ┘ │ │ │
28 ┘ │ │
29 ┘ │
29 ┘
35
reduce
4 9 8 5 2 1 0 6
13 13 3 6
26 9
35
Для использованиея reduce функтор необходимо переписать
#include <iostream>
#include <string_view>
#include <numeric>
using t_Accum = ::std::size_t;
class t_CountSpaces final
{
private: auto operator ()(char const ch) const noexcept
{
return (' ' == ch) ? t_Accum{1} : t_Accum{0};
}
public: auto operator ()(t_Accum const accum1, t_Accum const accum2) const noexcept
{
return accum1 + accum2;
}
public: auto operator ()(char const ch, t_Accum const accum) const noexcept
{
return accum + (*this)(ch);
}
public: auto operator ()(t_Accum const accum, char const ch) const noexcept
{
return accum + (*this)(ch);
}
public: auto operator ()(char const ch1, char const ch2) const noexcept
{
return (*this)(ch1) + (*this)(ch2);
}
};
auto CountWords(::std::string_view str)
{
return ::std::reduce(str.begin(), str.end(), t_Accum{0}, t_CountSpaces{});
}
int main()
{
// должно вывести 6
::std::cout << CountWords("pretty little octopus dnskdj kjanfkj snfs jknds") << ::std::endl;
return 0;
}
А еще стоит отметить, что ни то, ни другое не могут быть использованы в CountWords для подсчета количества слов. По сути тут считается не количество слов, а количество пробелов (для этого подошел бы ::std::count).