Autor Tema: TRUCO para que SWITCH opere con nombres o cadenas en lugar de numeros  (Leído 3573 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado CompSystems

  • PIC18
  • ****
  • Mensajes: 488
    • Home Page
TRUCO para que SWITCH opere con nombres o cadenas en lugar de numeros
« en: 19 de Noviembre de 2014, 00:48:19 »
En  C++ switch la variable de control solo puede ser numérica, entonces pensé que usando ENUM podría tener una lista de nombres para que luego SWITCH compara numéricamente, el siguiente código falla al leer la variable PERIF, por que? si yo definí un nuevo tipo type_perif

#include<iostream>
using namespace std;
int main() {

   //string perif;
   
   enum type_perif { Teclado =1 , teclado = 2, Raton = 3}; // ...
   type_perif perif;
   
   cout << "Escribe el nombre de un periférico del ordenador: " << endl;
   cin >> perif;
   switch (perif) {
   case "Teclado": case "teclado": case "Raton":
      cout << perif << " Es un periférico de entrada" << endl;
      break;
   case "Monitor": case "Impresora":
      cout << perif << ": Es un periférico de salida" << endl;
      break;
   case "Modem":
      cout << perif << ": Es un periférico de entrada/salida" << endl;
      break;
   default:
      cout << perif << ": Este periférico no lo conozco" << endl;
   }

   return 0;
}
Desde Colombia

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re: TRUCO para que SWITCH opere con nombres o cadenas en lugar de numeros
« Respuesta #1 en: 19 de Noviembre de 2014, 04:36:55 »
Por lo que tengo entendido solo pueden ser numeros.

Soluciones al problema que se dan ejemplos:
http://stackoverflow.com/questions/4165131/c-c-switch-for-non-integers
http://stackoverflow.com/questions/4480788/c-c-switch-case-with-string
http://stackoverflow.com/questions/650162/why-switch-statement-cannot-be-applied-on-strings  //En este ultimo hay un caso donde se usa enum

En fin es pasarlo a una constante
« Última modificación: 19 de Noviembre de 2014, 04:41:34 por KILLERJC »

Desconectado migsantiago

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8257
    • Sitio de MigSantiago
Re: TRUCO para que SWITCH opere con nombres o cadenas en lugar de numeros
« Respuesta #2 en: 19 de Noviembre de 2014, 11:55:22 »
Hola, debes convertir el texto que cin captura en enteros.

Otra opción es que uses mapas de C++ que usan un tipo de dato como entrada y entregan otro tipo de dato como salida.

http://www.cplusplus.com/reference/map/map/

Si gustas te puedo dar un ejemplo. Saludos.

http://www.cplusplus.com/reference/map/map/find/

Desconectado CompSystems

  • PIC18
  • ****
  • Mensajes: 488
    • Home Page
Re: TRUCO para que SWITCH opere con nombres o cadenas en lugar de numeros
« Respuesta #3 en: 21 de Noviembre de 2014, 00:37:51 »
Hola, debes convertir el texto que cin captura en enteros.
como?

Otra opción es que uses mapas de C++ que usan un tipo de dato como entrada y entregan otro tipo de dato como salida.


Si gustas te puedo dar un ejemplo. Saludos.

Claro bienvenido
Desde Colombia

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re: TRUCO para que SWITCH opere con nombres o cadenas en lugar de numeros
« Respuesta #4 en: 21 de Noviembre de 2014, 11:46:36 »
Veo que no miraste bien los ejemplos que te di. Con lo que dio migsantiago y un ejemplo de los que estan en los links que te pase te hubieras dado cuenta ahi nomas, hasta yo me di cuenta y no uso C++

En el 3er link:

Código: [Seleccionar]
#include <map>
#include <string>
#include <iostream.h>

// Aca definis tus valores
static enum StringValue { evNotDefined,
                          evStringValue1,
                          evStringValue2,
                          evStringValue3,
                          evEnd };

// Mapa para asociar las cadenas con los valores
static std::map<std::string, StringValue> s_mapStringValues;

// Entrada de ususario
static char szInput[_MAX_PATH];

// Inicializacion
static void Initialize();

int main(int argc, char* argv[])
{
  // Inicializar el mapa
  Initialize();

  // Loop hasta que termine el programa
  while(1)
  {
    // Tomar el dato de entrada
    cout << "Por favor ingrese la string (fin para terminar): ";
    cout.flush();
    cin.getline(szInput, _MAX_PATH);
    // Tomar el valor
    switch(s_mapStringValues[szInput])
    {
      case evStringValue1:
        cout << "Detectado el primer string." << endl;
        break;
      case evStringValue2:
        cout << "Detectado el segundo string." << endl;
        break;
      case evStringValue3:
        cout << "Detectado el tercero." << endl;
        break;
      case evEnd:
        cout << "Detectado string de fin de programa. "
             << "Programa detenido." << endl;
        return(0);
      default:
        cout << "'" << szInput
             << "' String no valida. s_mapStringValues Contiene "
             << s_mapStringValues.size()
             << " entradas." << endl;
        break;
    }
  }

  return 0;
}

void Initialize()
{
  s_mapStringValues["teclado"] = evStringValue1;
  s_mapStringValues["impresora"] = evStringValue2;
  s_mapStringValues["modem"] = evStringValue3;
  s_mapStringValues["fin"] = evEnd;

  cout << "s_mapStringValues contiene "
       << s_mapStringValues.size()
       << " entradas." << endl;
}

De tener que verificar por "Teclado" o "teclado", lo unico que haces es convertir la entrada a lowercase asi ambos son contenidos como "teclado".

Y para el caso de enteros podes usar algo que lo convierta en un hash y de alli compararlo con los hash de las palabras guardadas, pero la generacion del hash involucraria posiblidad de colicion y mas ciclos de maquina que tal ves no importe. O podrias directamente hacer una funcion con if/elseif y que devuelva un entero y dependiendo del entero hacer lo que tengas que hacer, sino aca lo hacen con enum (Parecido a lo que harias con enteros sin enum):

Código: [Seleccionar]
enum string_code {
    eFred,
    eBarney,
    eWilma,
    eBetty,
    ...
};

string_code hashit (std::string const& inString) {
    if (inString == "Fred") return eFred;
    if (inString == "Barney") return eBarney;
    ...
}

void foo() {
    switch (hashit(stringValue)) {
    case eFred:
        ...
    case eBarney:
        ...
    }
}

Donde stringValue es lo que recibis del usuario, eso lo manda a otra funcion que con unos if/else verifica si es uno de esas strings y retorna un valor definido en enum.
« Última modificación: 21 de Noviembre de 2014, 12:00:26 por KILLERJC »

Desconectado migsantiago

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8257
    • Sitio de MigSantiago
Re: TRUCO para que SWITCH opere con nombres o cadenas en lugar de numeros
« Respuesta #5 en: 21 de Noviembre de 2014, 16:41:34 »
Si gustas te puedo dar un ejemplo. Saludos.

Claro bienvenido

Hola, el camarada KillerJC ya te dio ejemplos de los links... estúdialos por favor. Creo que con ellos ya la libras.  ;-)

Desconectado Picuino

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 5892
    • Picuino
Re: TRUCO para que SWITCH opere con nombres o cadenas en lugar de numeros
« Respuesta #6 en: 21 de Noviembre de 2014, 18:12:25 »
Hay funciones hash rápidas y sin colisiones. Dependiendo del tamaño del conjunto de entrada, va a ser más rápido el if (para pocas palabras) o la función hash (para muchas palabras).

Para evitar las colisiones, puedes implementar un IF en el CASE que coincida con dos palabras de entrada. Estos IF sólo se ejecutan en pocas ocasiones y la función global sigue siendo rápida.

Por ejemplo un hash muy rápido puede hacerse con el XOR de los primeros 4 caracteres de la cadena de entrada.
Otro hash: suma de los caracteres
Otro hash: XOR de los caracteres y de la suma de los caracteres
Otro hash: XOR de los caracteres con rotación del resultado en 1 bit en cada operación

Hay muchos más, pero estos son bastante rápidos y funcionan bien para conjuntos pequeños.
Utiliza el que tenga menos colisiones para tu conjunto.


Saludos.
« Última modificación: 21 de Noviembre de 2014, 18:15:01 por Picuino »