【问题标题】:how to sum up a vector of vector int in C++ without loops如何在没有循环的情况下在 C++ 中总结向量 int 的向量
【发布时间】:2020-02-29 01:00:33
【问题描述】:

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

【问题讨论】:

  • I find it is hard for me to overload a Binary Operator in std::accumulate and implement it. 为什么?您尝试了哪些方法,为什么没有成功?
  • 没有循环就无法做到。 std::accumulate 里面有一个循环,通过使用算法,你只是在使用一个已经为你编码的循环。
  • 不要在std中超载你还需要展示一些工作。这不是一个教程网站。您需要就您尝试过的工作提出具体问题。
  • 您不会重载任何东西,您将创建的二进制操作传递给 std::accumulate。它可以是函数、lambda 或函数对象。
  • 基本问题是您需要一个循环,或者您必须在没有循环的情况下按顺序添加每个项目。 std::accumulate 使用循环。甚至专门的处理器指令也使用循环。那么,为什么要避免循环呢?

标签: c++ stdvector


【解决方案1】:

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

【讨论】:

  • 为什么在没有什么可捕获的情况下将 lambda 设置为通过引用自动捕获?
  • @Shloim 我的错,我已经习惯了。确实不需要。
【解决方案2】:

您需要使用std::accumulate 两次,一次用于外部vector 和二元运算符,该运算符知道如何使用对std::accumulate 的额外调用来对内部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
    }
);

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

    • 顺便说一句,问题说的是非循环方式。您的 for 语句是循环。
    • @ThomasMatthews 我写这个答案是为了反击不惜一切代价使用 STL 库的痴迷。我同意它不能按原样回答问题,但我发现它比让 OP 经历代码混乱的路径更好。此外,使用std::accumulate 也不能回答问题,因为它在其中使用了循环。如果我们遵循这个逻辑,那么定义一个函数sum 用循环完成这项工作,然后调用这个函数也将被认为是一个有效的答案。
    • @ThomasMatthews 阅读您的回答后,我看到我们都同意循环的事情,只是没有在回答的路上;)
    【解决方案4】:

    std::accumulate 的签名是:

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

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

    二元运算是:

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

    哪里...(来自cppreference)...

    Type1 类型必须使得T 类型的对象可以隐式转换为Type1Type2 类型必须使得InputIt 类型的对象可以被取消引用,然后隐式转换为Type2Ret 类型必须能够为 T 类型的对象分配 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&lt;int&gt;),一个用于内部向量(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);
    }
    

    【讨论】:

      【解决方案5】:

      基于嵌套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 数组。

      【讨论】:

      【解决方案6】:

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

      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。具有对数组求和的专门指令的处理器使用循环。循环是一种节省代码空间的便捷方法。但是,有时可能需要展开循环(出于性能原因)。您可以在其中包含展开或展开的内容的循环。

      【讨论】:

        【解决方案7】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-11-01
          • 2015-11-11
          • 2021-10-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多