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.
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 tipoT
pueda convertirse implícitamente enType1
. El tipoType2
debe ser tal que un objeto de tipoInputIt
pueda ser desreferenciado y luego convertido implícitamente aType2
. El tipoRet
debe ser tal que a un objeto de tipoT
se le pueda asignar un valor de tipoRet
.
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
}
);