Накопить абсолютные значения вектора - PullRequest
7 голосов
/ 07 октября 2019

Если мне нравится накапливать абсолютные значения std::vector, я могу использовать лямбду для вычисления абсолютного значения и добавить его к сумме std::accumulate ( live demo ).

#include <numeric>
#include <vector>

int main (){
    std::vector<int> vec {1,2,3,-4};
    auto abs_val =[](auto val, auto sum){return sum + std::fabs(val);};
    return std::accumulate(vec.begin(), vec.end(), 0, abs_val);
}

Я хотел бы написать

    return std::accumulate(vec.begin(), vec.end(), 0, std::fabs());

, но это не компилируется, поскольку ожидается функция с двумя аргументами sum и value.

Есть ли более элегантный способ написать это? Нужна ли лямбда? Могу ли я избавиться от этого?

Ответы [ 2 ]

6 голосов
/ 07 октября 2019

До C ++ 17

В основном вы хотите сделать две вещи: преобразовать элементы и затем суммировать их. Для std::accumulate вы должны указать алгоритму, как вы хотите суммировать элементы, но если вы хотите преобразовать элементы, вам нужно сделать что-то дополнительное.

Строка, которую вы хотите написать, говорит только о том, как преобразовать элементы (и она не компилируется, потому что accumulate ожидает функтор, который добавляет элементы, а не тот, который преобразует их).

TL;ДР: Нет. Если вы хотите преобразовать и добавить элементы, вы должны сделать оба. Не существует алгоритма с именем transform_and_accumulate, поэтому вы должны что-то комбинировать самостоятельно.

C ++ 17

Вышеприведенное верно только до C ++ 17,который имеет transform_reduce и который в основном делает то, что вы ищете.

3 голосов
/ 07 октября 2019

Есть две проблемы, связанные с тем, как вы хотели бы передавать потрясающие материалы. Первый тривиален, другой несколько более сложен. Код, который вы показали, не может работать, так как вы пытаетесь вызвать fabs и передать результат в std :: аккумуляцию (с плавающей запятой или double):

std::accumulate(vec.begin(), vec.end(), 0, std::fabs()); //note the parens ()

Так что, если std :: fabs была просто одной функциейи использовал правильную подпись, это сработало бы:

std::accumulate(vec.begin(), vec.end(), 0, std::fabs);

Тем не менее, как можно видеть здесь , fabs перегружен на float, double и long double, означая, что std :: fabs являетсянабор перегрузки, а не одна функция, и поэтому не ясно, какие версии адресов вы хотели бы передать. На эту часть вопроса есть ответ: Как указать указатель на перегруженную функцию?

Кроме того, как указано в комментариях и других ответах, накапливается последний параметр, ожидающий двоичный файлОперация, объединяющая два значения, тогда как fabs принимает абсолютное значение только один. Правильный алгоритм для использования в C ++ 17 transform_reduce :

std::transform_reduce(vec.begin(), vec.end(),0,std::plus<>{}, static_cast<double (*)(double)>(std::fabs));
...