trucos tipos significa relacionales que programacion operadores lenguaje existen español entre diferencias datos cuadro comparativo c++ comparison

c++ - significa - tipos de datos en c



Trucos de comparación en C++ (10)

Una clase:

class foo{ public: int data; };

Ahora quiero agregar un método a esta clase, para hacer una comparación, para ver si sus datos son iguales a uno de los números dados.

Por supuesto, puedo escribir if(data==num1|| data == num2|| data ==num3.....) , pero honestamente hablando, me siento mal cuando escribo data == cada vez que lo comparo un número.

Entonces, espero poder escribir algo como esto:

if(data is equal to one of these(num1,num2,num3,num4,num5...)) return true; else return false;

Quiero implementar esta declaración, los data is equal to one of these(num1, num2, num3, num4, num5...)

Aquí está mi enfoque:

#include <stdarg.h> bool is_equal_to_one_of_these(int count,...){ int i; bool equal = false; va_list arg_ptr; va_start(arg_prt,count); for(int x=0;x<count;x++){ i = va_arg(arg_ptr,int); if( i == data ){ equal = true; break; } } va_end(arg_ptr); return equal; }

Esta pieza de código hará el trabajo por mí. Pero cada vez que uso este método, tendré que contar los parámetros y pasarlo.

¿Alguien tiene una idea mejor?


¿Alguien tiene una mejor idea? gracias por compartir !

Hay un algitritmo estándar para eso:

using std::vector; // & std::begin && std::end // if(data is equal to one of these(1,2,3,4,5,6)) /* maybe static const */vector<int> criteria{ 1, 2, 3, 4, 5, 6 }; return end(criteria) != std::find(begin(criteria), end(criteria), data);

Editar: (todo en un solo lugar):

bool is_equal_to_one_of_these(int data, const std::vector<int>& criteria) { using std::end; using std::begin; using std::find; return end(criteria) != find(begin(criteria), end(criteria), data); } auto data_matches = is_equal_to_one_of_these(data, {1, 2, 3, 4, 5, 6});

Editar:

Prefiero la interfaz en términos de un vector, en lugar de una lista de inicializadores, porque es más poderosa:

std:vector<int> v = make_candidate_values_elsewhere(); auto data_matches = is_equal_to_one_of_these(data, v);

La interfaz (mediante el uso de un vector) no restringe la definición de los valores, donde se llama is_equal_to_one_of_these .


La manera fácil

El enfoque más simple es escribir un contenedor de funciones miembro llamado in() alrededor de std::find con un par de iteradores para buscar los datos en cuestión. Escribí una template<class It> in(It first, It last) simple template<class It> in(It first, It last) función de miembro template<class It> in(It first, It last) para ese

template<class It> bool in(It first, It last) const { return std::find(first, last, data) != last; }

Si no tiene acceso al origen de foo , puede escribir una función que no sea miembro de la template<class T> bool in(foo const&, std::initializer_list<T>) de firma template<class T> bool in(foo const&, std::initializer_list<T>) etc., y llámelo como

in(f, {1, 2, 3 });

El camino difícil

Pero vamos completamente al agua con eso: solo agrega dos sobrecargas public más:

  • uno tomando un parámetro std::initializer_list que llama al anterior con los iteradores begin() y end() del argumento correspondiente de la lista de inicializadores.
  • uno para un contenedor arbitrario como entrada que hará un pequeño envío de etiquetas a otras dos sobrecargas private de un detail_in() de detail_in() :
    • una sobrecarga haciendo un truco SFINAE con tipo de retorno final tipo decltype(c.find(data), bool()) que se eliminará del conjunto de sobrecarga si el contenedor c en cuestión no tiene una función miembro find() , y eso retorna bool contrario (esto se logra abusando del operador de coma dentro de decltype )
    • una sobrecarga de reserva que simplemente toma los iteradores begin() y end() y delegados al original in() teniendo dos iteradores

Debido a que las etiquetas para el detail_in() forman una jerarquía de herencia (muy similar a las etiquetas iterativas estándar), la primera sobrecarga coincidirá con los contenedores asociativos std::set y std::unordered_set y sus múltiples primos. Todos los demás contenedores, incluidos C-arrays, std::array , std::vector y std::list , coincidirán con la segunda sobrecarga.

#include <algorithm> #include <array> #include <initializer_list> #include <type_traits> #include <iostream> #include <set> #include <unordered_set> #include <vector> class foo { public: int data; template<class It> bool in(It first, It last) const { std::cout << "iterator overload: "; return std::find(first, last, data) != last; } template<class T> bool in(std::initializer_list<T> il) const { std::cout << "initializer_list overload: "; return in(begin(il), end(il)); } template<class Container> bool in(Container const& c) const { std::cout << "container overload: "; return detail_in(c, associative_container_tag{}); } private: struct sequence_container_tag {}; struct associative_container_tag: sequence_container_tag {}; template<class AssociativeContainer> auto detail_in(AssociativeContainer const& c, associative_container_tag) const -> decltype(c.find(data), bool()) { std::cout << "associative overload: "; return c.find(data) != end(c); } template<class SequenceContainer> bool detail_in(SequenceContainer const& c, sequence_container_tag) const { std::cout << "sequence overload: "; using std::begin; using std::end; return in(begin(c), end(c)); } }; int main() { foo f{1}; int a1[] = { 1, 2, 3}; int a2[] = { 2, 3, 4}; std::cout << f.in({1, 2, 3}) << "/n"; std::cout << f.in({2, 3, 4}) << "/n"; std::cout << f.in(std::begin(a1), std::end(a1)) << "/n"; std::cout << f.in(std::begin(a2), std::end(a2)) << "/n"; std::cout << f.in(a1) << "/n"; std::cout << f.in(a2) << "/n"; std::cout << f.in(std::array<int, 3>{ 1, 2, 3 }) << "/n"; std::cout << f.in(std::array<int, 3>{ 2, 3, 4 }) << "/n"; std::cout << f.in(std::vector<int>{ 1, 2, 3 }) << "/n"; std::cout << f.in(std::vector<int>{ 2, 3, 4 }) << "/n"; std::cout << f.in(std::set<int>{ 1, 2, 3 }) << "/n"; std::cout << f.in(std::set<int>{ 2, 3, 4 }) << "/n"; std::cout << f.in(std::unordered_set<int>{ 1, 2, 3 }) << "/n"; std::cout << f.in(std::unordered_set<int>{ 2, 3, 4 }) << "/n"; }

Ejemplo en vivo que, para todos los contenedores posibles , imprime 1 y 0 para ambos conjuntos de números.

Los casos de uso para la sobrecarga std::initializer_list son para la prueba de miembro-barco para pequeños conjuntos de números que usted escribe explícitamente en el código de llamada. Tiene complejidad O(N) pero evita cualquier asignación de pila.

Para tareas pesadas como la prueba de membresía de conjuntos grandes , puede almacenar los números en un contenedor asociativo como std::set , o sus primos multi_set o multi_set . Esto irá al montón cuando se guarden estos números, pero tiene una complejidad de búsqueda O(log N) o incluso O(1) .

Pero si tiene un contenedor de secuencia lleno de números, también puede enviarlo a la clase y calculará su membresía para usted en O(N) hora.


Cualquier optimización va a depender de las propiedades del conjunto de números que se comparan.

Si hay un límite superior definido, puede usar un std::bitset . Probar la pertenencia (es decir, la indexación en el conjunto de bits, que se comporta como una matriz), es O (1), en realidad unas pocas instrucciones rápidas. Esta es a menudo la mejor solución hasta cientos de límites, aunque dependiendo de la aplicación, millones podrían ser prácticos.


Hay muchas maneras de hacer esto con el STL.

Si tiene una cantidad increíblemente grande de elementos y desea probar si su artículo dado es miembro de este conjunto, use set o unordered_set . Le permiten verificar la membresía en log n y tiempo constante, respectivamente.

Si mantiene los elementos en una matriz ordenada, binary_search también probará la membresía en log n time.

Para matrices pequeñas, una search lineal puede preformarse significativamente más rápido (ya que no hay bifurcación). Una búsqueda lineal incluso puede hacer 3-8 comparaciones en el tiempo que le toma a la búsqueda binaria ''saltar'' . Esta publicación de blog sugiere que hay un punto de equilibrio en aproximadamente 64 elementos, por debajo de los cuales una búsqueda lineal puede ser más rápida, aunque esto obviamente depende de la implementación de STL, las optimizaciones del compilador y la predicción de bifurcación de su arquitectura.


Las respuestas usando std::initializer_list están bien, pero quiero agregar una solución más posible que es exactamente lo que intentas con esa C variadica de una manera segura y moderna: usando C ++ 11 plantillas variadas :

template<typename... NUMBERS> bool any_equal( const foo& f , NUMBERS&&... numbers ) { auto unpacked = { numbers... }; return std::find( std::begin( unpacked ) , std::end( unpacked ) , f.data ) != std::end( unpacked ); };

Por supuesto, esto solo funciona si todos los valores pasados ​​son del mismo tipo. Si no, la lista de inicializadores unpacked no se puede deducir ni inicializar.

Entonces:

bool equals = any_equal( f , 1,2,3,4,5 );

EDITAR: Aquí hay una are_same are_same para asegurar que todos los números pasados ​​sean del mismo tipo:

template<typename HEAD , typename... TAIL> struct are_same : public and_op<std::is_same<HEAD,TAIL>::value...> {};

Donde and_op realiza n-ary logical y:

template<bool HEAD , bool... TAIL> struct and_op : public std::integral_constant<bool,HEAD && and_op<TAIL...>::value> {}; template<> struct and_op<> : public std::true_type {};

Esto hace posible forzar el uso de números del mismo tipo de una manera simple:

template<typename... NUMBERS> bool any_equal( const foo& f , NUMBERS&&... numbers ) { static_assert( all_same<NUMBERS...>::value , "ERROR: You should use numbers of the same type" ); auto unpacked = { numbers... }; return std::find( std::begin( unpacked ) , std::end( unpacked ) , f.data ) != std::end( unpacked ); };


No es bonito, pero esto debería funcionar:

class foo { bool equals(int a) { return a == data; } bool equals(int a, int b) { return (a == data) || (b == data); } bool equals(int a, int b, int c) {...} bool equals(int a, int b, int c, int d) {...} private: int data; }

Y así. Eso te dará la sintaxis exacta que estabas buscando. Pero si busca la cantidad de argumentos completamente variable, entonces el vector o la lista std :: initalizer podrían ser el camino a seguir:

Ver: http://en.cppreference.com/w/cpp/utility/initializer_list

Este ejemplo lo muestra en acción:

#include <assert.h> #include <initializer_list> class foo { public: foo(int d) : data(d) {} bool equals_one_of(std::initializer_list<int> options) { for (auto o: options) { if (o == data) return true; } return false; } private: int data; }; int main() { foo f(10); assert(f.equals_one_of({1,3,5,7,8,10,14})); assert(!f.equals_one_of({3,6,14})); return 0; }


Si data, num1, .. num6 están entre 0 y 31, entonces puede usar

int match = ((1<<num1) | (1<<num2) | ... | (1 << num6)); if( ( (1 << data) & match ) != 0 ) ...

Si num1 a num6 son constantes, el compilador calculará la coincidencia en tiempo de compilación.


Si los data son realmente un tipo integral o enumerado, puede usar un switch :

switch (data) { case 1: case 2: case 2000: case 6000: case /* whatever other values you want */: act_on_the_group(); break; default: act_on_not_the_group(); break; }


Yo recomendaría usar contenedor estándar como std::vector , pero eso aún implicaría una complejidad lineal con el peor tiempo de ejecución de O(N) .

class Foo{ public: int data; bool is_equal_to_one_of_these(const std::vector<int>& arguments){ bool matched = false; for(int arg : arguments){ //if you are not using C++11: for(int i = 0; i < arguments.size(); i++){ if( arg == data ){ //if you are not using C++11: if(arguments[i] == data){ matched = true; } } return matched; } }; std::vector<int> exampleRange{ {1,2,3,4,5} }; Foo f; f.data = 3; std::cout << f.is_equal_to_one_of_these(exampleRange); // prints "true"


set es una buena opción, pero si realmente quieres hacer la tuya, initializer_list es conveniente:

bool is_in( int val, initializer_list<int> lst ) { for( auto i : lst ) if( i == val ) return true; return false; }

el uso es trivial:

is_in( x, { 3, 5, 7 } ) ;

es O (n) tú, conjunto / desordenado es más rápido