Aquí podría ser tu PUBLICIDAD


C ++ comparando un montón de valores con uno dado

votos
3

Necesito comparar un valor dado con un valor recuperado. Lo hago varias veces en el código. No estoy satisfecho con cómo se ve y estoy buscando una especie de función de utilidad. ¿Alguien escribió uno?

El número de valores con los que estoy comparando se conoce en el momento de la compilación.

Actualización: me gustaría deshacerme de los contenedores ya que sé la cantidad exacta de valores (a menudo no más de 3) con los que quiero comparar. Y no es tan conveniente poner artículos en el contenedor todo el tiempo.
No amo si no porque no es obvio como encontrar.

#include <algorithm>
#include <string>
#include <vector>

std::string getValue1()
{
    return test;
}

std::string getValue2()
{
    return the;
}

std::string getValue3()
{
    return world;
}

int main()
{
    const std::string value = the;

    // simple if
    if ( value == getValue1() ||
         value == getValue2() ||
         value == getValue3() )
        return 1;

    // using collections like vector, set
    std::vector<std::string> values;
    values.push_back( getValue1() );
    values.push_back( getValue2() );
    values.push_back( getValue3() );
    if ( values.end() != std::find( values.begin(), values.end(), value ) )
        return 1;

    // third option I'd use instead
    //

    return 0;
}
Publicado el 12/03/2009 a las 17:47
fuente por usuario Mykola Golubyev
En otros idiomas...        العربية       

10 respuestas

votos
7

Si los valores que está buscando son Comparables con operator <(como ints, float y std :: strings), entonces es más rápido usar un std :: set para poner los valores allí y luego marcar set.find (value) = = set.end (). Esto se debe a que el conjunto almacenará los valores con un cierto orden que permite búsquedas más rápidas. Usar una tabla hash será aún más rápido. Sin embargo, por menos de 50 valores, es posible que no note ninguna diferencia :) Así que mi regla general sería:

  • Menos de 5 elementos: si con múltiples ||

  • 5 o más: poner en una mesa set o hash

Respondida el 12/03/2009 a las 05:55
fuente por usuario Joao da Silva


Aquí podría ser tu PUBLICIDAD


votos
3

puede escribir un conjunto de funciones de plantilla que lo ayudarán a superar esto, por ejemplo:

template <typename T>
bool InSet(const T & item, const T & i1, const T & i2) {
  return item==i1 || item==i2;
}

template <typename T>
bool InSet(const T & item, const T & i1, const T & i2, const T & i3) {
  return item==i1 || item==i2 || item==i3;
}

Tenga en cuenta que puede hacer que InSet funcione como si tomara una cantidad variable de argumentos creando varias plantillas con diferentes números de argumentos.

Y entonces:

int i;
if (InSet(i, 3, 4, 5)) { ... }
string s;
if (InSet(s, "foobar", "zap", "garblex")) { ... }

etc.

Respondida el 12/03/2009 a las 06:14
fuente por usuario Antti Huima

votos
1

¿Qué cambia más, el 'valor' o los valores devueltos por 'getValueX ()'? Puede insertar todo en un hash_map / map y luego hacer una búsqueda de esa manera, como ha sugerido con los contenedores.

Respondida el 12/03/2009 a las 06:37
fuente por usuario wchung

votos
1

Por su solicitud de hacer

if (InSet(value)(GetValue1(), GetValue2(), GetValue3()))
{
   // Do something here...
}

Prueba esto:

template <typename T>
class InSetHelper
{
     const T &Value;
     void operator=(const InSetHelper &);
public:
     InSetHelper(const T &value) : Value(value) {}

     template<class Other, class Another>
     bool operator()(const Other &value1, const Another &value2) const
     {
         return Value == value1 || Value == value2;
     }
     template<class Other, class Another, class AThird>
     bool operator()(const Other &value1, const Another &value2, const AThird &value3) const
     {
         return Value == value1 || Value == value2 || Value == value3;
     }
};

template <typename T> 
InSetHelper<T> InSet(const T &value) { return InSetHelper<T>(value); }

Sin embargo, esta sintaxis podría ser más clara:

if (MakeSet(GetValue1(), GetValue2(), GetValue3()).Contains(value))
{
   // Do something here...
}

template <typename T, typename U, typename V>
class Set3
{
    const T& V1;
    const U& V2;
    const V& V3;
    void operator=(const Set3 &);
public:
    Set3(const T &v1, const U &v2, const V &v3) : V1(v1), V2(v2), V3(v3) {}

    template <typename W>
    bool Contains(const W &v) const
    {
        return V1 == v || V2 == v || V3 == v;
    }
};

template <typename T, typename U>
class Set2 
{ 
     // as above 
};

template <typename T, typename U, typename V>
Set3<T, U, V> MakeSet(const T &v1, const U &v2, const V &v3)
{
    return Set3<T, U, V>(v1, v2, v3);
}

template <typename T, typename U>
Set3<T, U> MakeSet(const T &v1, const U &v23)
{
    return Set3<T, U, V>(v1, v2);
}

Si esos valores son realmente parte de un árbol o una lista vinculada, entonces ya tienes tu conjunto / contenedor, y tu mejor opción es usar alguna recursividad:

parent.ThisOrDescendantHasValue(value);

Simplemente agregue esto a cualquier clase a la que pertenezcan padres e hijos:

class Node
{
public: 
    Value GetValue();
    Node *GetChild();
    bool ThisOrDescendantHasValue(const Value &value)
    {
        return GetValue() == value
           || (GetChild() && GetChild->ThisOrDescendantHasValue(value));
    }
};
Respondida el 12/03/2009 a las 06:32
fuente por usuario Eclipse

votos
1

No necesita un std :: set o un std :: vector . Solo use std :: set_intersection () ...

El código es mejor ...

#include <set>
#include <iostream>
#include <iterator>
using namespace std;

#define COUNT(TYPE,ARRAY)  ( sizeof(ARRAY) / sizeof(TYPE) )

inline bool CaseInsensitiveCompare (const char * a, const char * b)
  {  return strcasecmp( a, b ) < 0;  }

int  main()
{
  const char * setA[] = { "the", "world", "is", "flat" };
  const char * setB[] = { "the", "empty", "set", "is", "boring" };

  stable_sort( setA,  setA + COUNT( const char *, setA ),
               CaseInsensitiveCompare );

  stable_sort( setB,  setB + COUNT( const char *, setB ),
               CaseInsensitiveCompare );

  cout << "Intersection of sets:  ";
  set_intersection( setA, setA + COUNT( const char *, setA ),
                    setB, setB + COUNT( const char *, setB ),
                    ostream_iterator<const char *>(cout, " "),
                    CaseInsensitiveCompare );
  cout << endl << endl;
}

O tal vez, dado el problema de búsqueda de 1-N:
(Nota: ¡use binary_search () DESPUÉS de ordenar!)

if ( binary_search( setA, setA + COUNT( const char *, setA ),
            "is", CaseInsensitiveCompare ) )
  ...

if ( binary_search( setA, setA + COUNT( const char *, setA ),
            "set", CaseInsensitiveCompare ) )
  ...
Respondida el 12/03/2009 a las 06:16
fuente por usuario Mr.Ree

votos
0

¿Qué le parece usar un boost :: array (o std :: tr1 :: array) y crear una función simple como esta:

template <typename ValueType, size_t arraySize>
bool contains(const boost::array<ValueType, arraySize>& arr, const ValueType& val)
{
    return std::find(arr.begin(), arr.end(), val)!=arr.end();
}

A continuación, puede volver a utilizar eso con bastante facilidad:

#include <string>
#include <iostream>

#include <boost\array.hpp>

template <typename ValueType, size_t arraySize>
bool contains(const boost::array<ValueType, arraySize>& arr, const ValueType& val)
{
    return std::find(arr.begin(), arr.end(), val)!=arr.end();
}

int _tmain(int argc, _TCHAR* argv[])
{
    boost::array<std::string, 3> arr = {"HI", "there", "world"};

    std::cout << std::boolalpha 
        << "arr contains HI: " << contains(arr, std::string("HI")) << std::endl
        << "arr contains blag: " << contains(arr, std::string("blag") ) << std::endl
        << "arr contains there: " << contains(arr, std::string("there") ) << std::endl;

    return 0;
}

Editar: Así que el impulso está fuera. Es bastante fácil adaptar esto a una matriz regular:

template <typename ValueType, size_t arraySize>
bool contains(ValueType (&arr)[arraySize], const ValueType& val)
{
    return std::find(&arr[0], &arr[arraySize], val)!=&arr[arraySize];
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::string arr[3] = {"HI", "there", "world"};

    std::cout << std::boolalpha << "arr contains HI: " << contains(arr, std::string("HI")) << std::endl
        << "arr contains blag: " << contains(arr, std::string("blag") ) << std::endl
        << "arr contains there: " << contains(arr, std::string("there") ) << std::endl;

    return 0;
}
Respondida el 12/03/2009 a las 06:41
fuente por usuario zdan

votos
0

Depende de la fuente de los valores recuperados, si está leyendo desde un archivo o secuencia, entonces haría algo diferente, pero si su fuente es una serie de funciones, la siguiente es una forma diferente de hacerlo, no es perfecta. pero puede adaptarse a sus necesidades:

const int count = 3;
std::string value = "world";
boost::function<std::string(void)> funcArray[count];
funcArray[0] = &getValue1;
funcArray[1] = &getValue2;
funcArray[2] = &getValue3;

for( int i = 0; i < count; ++i )
{
    if( funcArray[i]() == value )
        return 1;
}

Si sabe qué funciones son la fuente (así como el conteo de objetos), espero que pueda armar la matriz de punteros de función utilizando el preprocesador.

Respondida el 12/03/2009 a las 06:24
fuente por usuario Patrick

votos
0

me gusta el enfoque de colecciones, tal vez use un hash_set en lugar de un vector. almacene los valores en un archivo de propiedades y tenga un método para poblar el hash_set del archivo, y otro para devolver un valor booleano si el valor está en el hash_set. entonces puedes bajar a una 2 líneas en el código principal.

Respondida el 12/03/2009 a las 05:55
fuente por usuario John Ellinwood

votos
0

Recomendaría su método 2, usando un std :: vector u otro contenedor y buscando membresía. Dado que el orden de los elementos con los que está comprobando probablemente no sea relevante, puede usar un std :: set o std :: map. Si tiene muchos elementos en su conjunto de valores, entonces usar un conjunto o un mapa será más rápido, mientras que si tiene pocos, un vector puede ser más rápido.

La ventaja de cualquiera de estos enfoques es que puede almacenar el conjunto / mapa en algún lugar común, y evitar tener que construir el conjunto de respuestas coincidentes cada vez.

Respondida el 12/03/2009 a las 05:53
fuente por usuario JSBձոգչ

votos
0

std::find_first_of.

Por supuesto, si solo te interesa encontrar un único valor, puedes crear tu propio contenedor std::find.

Respondida el 12/03/2009 a las 05:52
fuente por usuario MSN


Aquí podría ser tu PUBLICIDAD