как суммировать вектор вектора int в C ++ без циклов - PullRequest
3 голосов
/ 03 ноября 2019

Я пытаюсь реализовать это суммирование всех элементов vector<vector<int>> нецикличными способами.
Ранее я проверил некоторые важные вопросы, Как суммировать элементы вектора C ++? .
Поэтому я пытаюсь использовать std::accumulate для его реализации, но мне трудно перегрузить Binary Operator в std::accumulate и реализовать его.
Так что я не совсем понимаю, как его реализовать. с std::accumulate или есть лучший способ?
Если не ум, кто-нибудь может мне помочь?
Заранее спасибо.

Ответы [ 7 ]

7 голосов
/ 03 ноября 2019

Вам нужно использовать std::accumulate дважды, один раз для внешнего vector с двоичным оператором, который знает, как суммировать внутреннее vector, используя дополнительный вызов std::accumulate:

int sum = std::accumulate(
    vec.begin(), vec.end(),                       // iterators for the outer vector
    0,                                            // initial value for summation - 0
    [](int init, const std::vector<int>& intvec){ // binaryOp that sums a single vector<int>
        return std::accumulate(
            intvec.begin(), intvec.end(), // iterators for the inner vector
            init);                        // current sum
                                          // use the default binaryOp here
    }
);
5 голосов
/ 03 ноября 2019

В этом случае я не рекомендую использовать std::accumulate, поскольку это сильно ухудшит читабельность. Более того, эта функция использует циклы внутри, поэтому вы ничего не сохраните. Просто сравните следующее решение, основанное на циклах, с другими ответами, которые используют std::accumulate:

int result = 0 ;
for (auto const & subvector : your_vector)
    for (int element : subvector)
        result += element;

. Позволяет ли использование кода итераторов, функций STL и лямбда-функций сделать ваш код легче для понимания и быстрее? Для меня ответ ясен. Петли не являются злом, особенно для такого простого применения.

2 голосов
/ 03 ноября 2019

Подпись std::accumulate:

T accumulate( InputIt first, InputIt last, T init,
              BinaryOperation op );

Обратите внимание, что возвращаемое значение выводится из параметра init (это не обязательно value_type из InputIt).

Двоичная операция:

Ret binary_op(const Type1 &a, const Type2 &b);

, где ... (из cppreference ) ...

Тип Type1 должен бытьтакой, что объект типа T может быть неявно преобразован в Type1. Тип Type2 должен быть таким, чтобы объект типа InputIt мог быть разыменован, а затем неявно преобразован в Type2. Тип Ret должен быть таким, чтобы объекту типа T можно было присвоить значение типа Ret.

Однако, когда T является value_type из InputIt, вышеприведенное проще и у вас есть:

using value_type = std::iterator_traits<InputIt>::value_type;
T binary_op(T,value_type&).

Предполагается, что ваш конечный результат будет int, следовательно, T равно int. Вам нужно два вызова: два std::accumulate, один для внешнего вектора (где value_type == std::vector<int>) и один для внутренних векторов (где value_type == int):

#include <iostream>
#include <numeric>
#include <iterator>
#include <vector>

template <typename IT, typename T>
T accumulate2d(IT outer_begin, IT outer_end,const T& init){
    using value_type = typename std::iterator_traits<IT>::value_type;
    return std::accumulate( outer_begin,outer_end,init,
        [](T accu,const value_type& inner){
            return std::accumulate( inner.begin(),inner.end(),accu);
        });
}

int main() {
    std::vector<std::vector<int>> x{ {1,2} , {1,2,3} };
    std::cout << accumulate2d(x.begin(),x.end(),0);
}
2 голосов
/ 03 ноября 2019

Согласно https://en.cppreference.com/w/cpp/algorithm/accumulate, похоже, что BinaryOp имеет текущую сумму слева, а следующий элемент диапазона - справа. Таким образом, вы должны запустить std :: аккумулировать с правым аргументом, а затем просто сложить его с левым аргументом и вернуть результат. Если вы используете C ++ 14 или более позднюю версию,

auto binary_op = [&](auto cur_sum, const auto& el){
    auto rhs_sum = std::accumulate(el.begin(), el.end(), 0);
    return cur_sum + rhs_sum;
};

Я не пытался скомпилировать код :). Если я испортил порядок аргументов, просто замените их.

Редактировать: неверная терминология - вы не перегружаете BinaryOp, вы просто передаете его.

1 голос
/ 03 ноября 2019

С range-v3 (и вскоре с C ++ 20) вы можете сделать

const std::vector<std::vector<int>> v{{1, 2}, {3, 4, 5, 6}};
auto flat = v | ranges::view::join;
std::cout << std::accumulate(begin(flat), end(flat), 0);

Demo

1 голос
/ 03 ноября 2019

Чтобы избежать циклов, вам нужно специально добавить каждый элемент:

std::vector<int> database = {1, 2, 3, 4};
int sum = 0;
int index = 0;
// Start the accumulation
sum = database[index++];
sum = database[index++];
sum = database[index++];
sum = database[index++];

Нет гарантии, что std::accumulate будет нецикличным (без циклов). Если вам нужно избегать петель, не используйте его.

ИМХО, в использовании циклов нет ничего плохого: for, while или do-while. Процессоры, которые имеют специальные инструкции для суммирования массивов, используют циклы. Циклы - это удобный метод для сохранения пространства кода. Однако могут быть случаи, когда циклы хотят быть развернутыми (по соображениям производительности). Вы можете иметь цикл с развернутым или развернутым содержимым.

1 голос
/ 03 ноября 2019

Решения, основанные на вложенности std::accumulate может быть трудно понять.

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

int main()
   {

   // create a unary operator for 'std::transform'
   auto accumulate = []( vector<int> const & v ) -> int
      {
      return std::accumulate(v.begin(),v.end(),int{});
      };

   vector<vector<int>> data = {{1,2,3},{4,5},{6,7,8,9}}; // 2D array

   vector<int> temp; // 1D array of intermediate sums
   transform( data.begin(), data.end(), back_inserter(temp), accumulate );
   int result = accumulate(temp);

   cerr<<"result="<<result<<"\n";

   }

При вызове transform накапливается каждый из внутренних массивов для инициализации массива 1D temp.

...