c++ stdvector

cómo resumir un vector de vector int en C++ sin bucles



stdvector (7)

Intento implementar esa suma de todos los elementos de un vector<vector<int>> de forma no circular.
He revisado algunas preguntas relevantes antes, ¿Cómo resumir elementos de un vector C ++? .
Así que trato de usar std::accumulate para implementarlo, pero me resulta difícil sobrecargar un Binary Operator en std::accumulate e implementarlo.
Entonces, estoy confundido acerca de cómo implementarlo con std::accumulate o ¿hay una mejor manera?
Si no importa, ¿alguien podría ayudarme?
Gracias por adelantado.


Con range-v3 (y pronto con C ++ 20), puede hacer

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


De acuerdo con https://en.cppreference.com/w/cpp/algorithm/accumulate , parece que BinaryOp tiene la suma actual en la mano izquierda y el siguiente elemento de rango a la derecha. Por lo tanto, debe ejecutar std :: acumular en el argumento del lado derecho, y luego sumarlo con el argumento del lado izquierdo y devolver el resultado. Si usa C ++ 14 o posterior,

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

Sin embargo, no intenté compilar el código :). Si estropeé el orden de los argumentos, simplemente reemplácelos.

Editar: terminología incorrecta: no sobrecarga BinaryOp, simplemente lo pasa.


En este caso, no sugiero usar std::accumulate ya que perjudicaría enormemente la legibilidad. Además, esta función usa bucles internamente, por lo que no guardaría nada. Simplemente compare la siguiente solución basada en bucle con las otras respuestas que usan std::accumulate :

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

¿El uso de una combinación de iteradores, funciones STL y funciones lambda hace que su código sea más fácil de entender y más rápido? Para mí, la respuesta es clara. Los bucles no son malos, especialmente para una aplicación tan simple.


Firma de std::accumulate es:

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

Tenga en cuenta que el valor de retorno se deduce del parámetro init (no es necesariamente el value_type de InputIt ).

La operación binaria es:

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

donde ... (de https://en.cppreference.com/w/cpp/algorithm/accumulate ) ...

El tipo Type1 debe ser tal que un objeto de tipo T pueda convertirse implícitamente en Type1 . El tipo Type2 debe ser tal que un objeto de tipo InputIt pueda ser desreferenciado y luego convertido implícitamente a Type2 . El tipo Ret debe ser tal que a un objeto de tipo T se le pueda asignar un valor de tipo Ret .

Sin embargo, cuando T es el value_type de InputIt , lo anterior es más simple y tiene:

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

Se supone que su resultado final es un int , por lo tanto, T es int . Necesita dos llamadas dos std::accumulate , una para el vector externo (donde value_type == std::vector<int> ) y otra para los vectores internos (donde 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); }


Las soluciones basadas en std::accumulate pueden ser difíciles de entender.

Al usar una matriz 1D de sumas intermedias, la solución puede ser más sencilla (pero posiblemente menos eficiente).

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

La llamada a la transform acumula cada una de las matrices internas para inicializar la matriz temp 1D.


Para evitar bucles, deberá agregar específicamente cada elemento:

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++];

No hay garantía de que std::accumulate sea ​​sin bucle (sin bucles). Si necesita evitar bucles, no lo use.

En mi humilde opinión, no hay nada de malo en usar bucles: for , while o do-while . Los procesadores que tienen instrucciones especializadas para sumar matrices usan bucles. Los bucles son un método conveniente para conservar el espacio de código. Sin embargo, puede haber ocasiones en que los bucles quieran desenrollarse (por razones de rendimiento). Puede tener un bucle con contenido expandido o desenrollado.


std::accumulate usar std::accumulate dos veces, una vez para el vector externo con un operador binario que sepa cómo sumar el vector interno mediante una llamada adicional a 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 } );