Descomponer dos vectores
Espero que la palabra "descomponer" sea correcta, pero el problema es simple: obtuve dos listas después de una operación y quiero saber qué cambio sucedió de una lista a otra. Como tal, quiero "descomponer" las dos listas A y B en "Ambos", "Sólo A" y "Sólo B".
template <class T>
void decompose(std::vector<T*> &a, std::vector<T*> &b, std::vector<T*> &only_a, std::vector<T*> &only_b, std::vector<T*> &both) {
only_a = a;
only_b = b;
for (T* x : a) {
for (T* y : b) {
if (x == y) {
both.push_back(x);
}
}
}
{
auto it = only_a.begin();
while (it != only_a.end()) {
bool erase = false;
for (T* x : both) {
if (x == *it) {
it = only_a.erase(it);
erase = true;
}
}
if (!erase) {
it++;
}
}
}
{
auto it = only_b.begin();
while(it != only_b.end()) {
bool erase = false;
for (T* x : both) {
if (x == *it) {
it = only_b.erase(it);
erase = true;
}
}
if (!erase) {
it++;
}
}
}
}
Siento que debería haber una forma más rápida de hacer esto que tres bucles entrelazados dos veces.
Respuestas
Para algo fácil de leer y mantener que usaría set_differencey set_intersectionque funcionaría bien en rangos ordenados sin duplicados:
std::set_intersection(a.begin(), a.end(), b.begin(), b.end(), std::back_inserter(both));
only_a.reserve(a.size() - both.size());
std::set_difference(a.begin(), a.end(), b.begin(), b.end(), std::back_inserter(only_a));
only_b.reserve(b.size() - both.size());
std::set_difference(b.begin(), b.end(), a.begin(), a.end(), std::back_inserter(only_b));
... pero eso requiere que repita los rangos tres veces, y creo que está buscando algo más eficiente.
Primero, no empezaría copiando ay ben only_ay only_brespectivamente. En su lugar, inspírese en las implementaciones de ejemplo para las funciones estándar que vinculé anteriormente y cree su propio algoritmo similar. Esto requiere que Tse pueda comparar s con operator<:
#include <algorithm>
#include <iterator>
template <class T>
void decompose(std::vector<T>& a,
std::vector<T>& b,
std::vector<T>& only_a,
std::vector<T>& only_b,
std::vector<T>& both)
{
// Sort the input or require the input to be sorted like some algorithms do
// If you'd like the input to be unchanged, make a and b const and make
// copies of them instead and sort those copies.
std::sort(a.begin(), a.end());
std::sort(b.begin(), b.end());
// clear destination vectors or skip this if you want to append instead
only_a.clear();
only_b.clear();
// the actual algorithm - loop for as long as both vectors have elements
auto ait = a.begin();
auto bit = b.begin();
while(ait != a.end() && bit != b.end()) {
if(*ait < *bit) {
only_a.push_back(*ait++); // can only be in a
} else if(*bit < *ait) {
only_b.push_back(*bit++); // can only be in b
} else {
both.push_back(*ait++); // must be in both
++bit;
}
}
// Add the remaining elements if not both ait and bit have reached their end()
if(ait != a.end()) std::copy(ait, a.end(), std::back_inserter(only_a));
else if(bit != b.end()) std::copy(bit, b.end(), std::back_inserter(only_b));
}
O hacerlo aún más genérico y dejar que funcione solo con iteradores y agregar la posibilidad de que el usuario proporcione un functor de comparación . Esto requiere que los rangos se ordenen en el mismo orden en que lo estarían si se usara el functor de comparación con std::sorten los rangos. El functor de comparación predeterminado está aquí std::less<>que, si no está especializado para el tipo involucrado, utiliza operator<para comparar los elementos.
#include <functional> // less
#include <iterator> // iterator_traits
template <
class First1, class Last1, class First2, class Last2,
class OnlyAinserter, class OnlyBinserter, class BothInserter,
class Comp = std::less<typename std::iterator_traits<First1>::value_type>
// class Comp = std::less<> // <- is sufficient in C++14 and forward
>
void decompose(First1 ait, Last1 aend, First2 bit, Last2 bend,
OnlyAinserter onlyait, OnlyBinserter onlybit, BothInserter bothit,
Comp comp = Comp{})
{
// loop for as long as both vectors have elements
while(ait != aend && bit != bend) {
if(comp(*ait, *bit)) {
*onlyait++ = *ait++; // can only be in a
} else if(comp(*bit, *ait)) {
*onlybit++ = *bit++; // can only be in b
} else {
*bothit++ = *ait++; // must be in both
++bit;
}
}
// Add the remaining elements if not both ait and bit have reached aend/bend
if(ait != aend) std::copy(ait, aend, onlyait);
else if(bit != bend) std::copy(bit, bend, onlybit);
}
Que luego se puede llamar así usando el functor de comparación predeterminado :
decompose(a.begin(), a.end(), b.begin(), b.end(),
std::back_inserter(only_a), std::back_inserter(only_b), std::back_inserter(both));
O como a continuación, proporcionando un functor de comparación . En este ejemplo, los rangos deben ordenarse en orden descendente:
decompose(a.begin(), a.end(), b.begin(), b.end(),
std::back_inserter(only_a), std::back_inserter(only_b), std::back_inserter(both),
[](auto& A, auto& B) { return A > B; } // std::greater<>
);