如何在没有循环的情况下在 C++ 中总结 vector int 的向量

how to sum up a vector of vector int in C++ without loops

我尝试以非循环方式实现对 vector<vector<int>> 的所有元素求和。
之前查过一些相关问题,How to sum up elements of a C++ vector?.
所以我尝试使用 std::accumulate 来实现它,但我发现我很难在 std::accumulate 中重载 Binary Operator 并实现它。
所以我对如何用 std::accumulate 实现它感到困惑,或者有更好的方法吗?
如果不介意谁能帮帮我?
提前致谢。

根据 https://en.cppreference.com/w/cpp/algorithm/accumulate ,看起来 BinaryOp 在左侧有当前总和,在右侧有下一个范围元素。所以你应该 运行 std::accumulate 在右侧参数上,然后将它与左侧参数和 return 结果相加。如果您使用 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,你只是传递它。

您需要使用 std::accumulate 两次,一次用于外部 vector 和一个二元运算符,该运算符知道如何使用对 [=11= 的额外调用对内部 vector 求和]:

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
    }
);

在这种情况下,我不建议使用std::accumulate,因为它会大大影响可读性。此外,此函数在内部使用循环,因此您不会保存任何内容。只需将以下基于循环的解决方案与使用 std::accumulate:

的其他答案进行比较
int result = 0 ;
for (auto const & subvector : your_vector)
    for (int element : subvector)
        result += element;

结合使用迭代器、STL 函数和 lambda 函数是否会使您的代码更易于理解且速度更快?对我来说,答案很明确。循环并不邪恶,尤其是对于这种简单的应用程序。

std::accumulate 的签名是:

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

注意return值是从init参数推导出来的(不一定是InputItvalue_type)。

二进制运算为:

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

其中...(来自 cppreference)...

The type Type1 must be such that an object of type T can be implicitly converted to Type1. The type Type2 must be such that an object of type InputIt can be dereferenced and then implicitly converted to Type2. The type Ret must be such that an object of type T can be assigned a value of type Ret.

然而,当TInputItvalue_type时,以上更简单,你有:

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

您的最终结果应该是 int,因此 Tint。您需要两次调用两次 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);
}

基于嵌套的解决方案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 的调用累积每个内部数组以初始化一维 temp 数组。

为避免循环,您必须专门添加每个元素:

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会是非循环的(没有循环)。如果您需要避免循环,则不要使用它。

恕我直言,使用循环没有任何问题:forwhiledo-while。具有用于对数组求和的专门指令的处理器使用循环。循环是保存代码的便捷方法 space。但是,有时可能需要 展开 循环(出于性能原因)。您可以有一个循环,其中包含展开或展开的内容。

使用 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