2010-08-30 9 views
5

Quiero cambiar algún código de Perl en C++. Necesito saber cómo implementar hashes de Perl anidados en C++. Pensé en STL como una buena opción y usé mapas. Con la ayuda de mapas, puedo crear solo un hash simple, pero no sé cómo crear una estructura hash anidada.¿Cómo implemento el equivalente de hashes de Perl anidados en C++?

Mi Perl hash es así:

%foo = (
    "bar1" => { 
     Default => 0, 
     Value => 0 
    }, 
    "bar2" => { 
     Default => 2, 
     value => 5, 
     other => 4 
    } 
) 

puedo modificarlo así: $foo{"bar1"}->{"Default"} = 15.

¿Cómo puedo hacer esto en C++ usando STL? Tal vez esta es una pregunta simple, pero no puedo resolverlo.

+2

Nota de respuestas aún referencia a std :: mapa no dará lugar a las tablas hash. Estos son contenedores asociativos basados ​​en BTree. Debe ir a Boost Unordered, o al nuevo STL, que incluye tablas hash de forma nativa. –

+0

Esto resultó ser una pregunta sorprendentemente entretenido. Buen trabajo @saran –

Respuesta

2

Puede tener hashes anidados con std::map<key_t,std::map<key_t,value_t> >.

+0

he editado el post para añadir un espacio entre la >>, como que, lamentablemente, no se compilará (se piensa que es el operador >>). Si no me equivoco 0x va a arreglar eso, pero hasta entonces ... –

+0

He estado pensando en ello, se "fija" en 0x pero en este caso es mejor ser claro; Gracias. –

4

Es posible que necesite el tipo:

std::map< std::string, std::map<std::string, int> > 

Es posible que necesite usar struct (o class) en su lugar.

struct Element { 
    int default; 
    int value; 
    int other; 
    Element(): default(0), value(0), other(0) 
    { } 
    Element(int default_, int value_, int other_) 
    : default(default_) 
    , value(value_) 
    , other(other_) 
    { } 
}; 

int main() { 
    std::map<std::string, Element> elements; 
    elements["bar1"]; // Creates element with default constructor 
    elements["bar2"] = Element(2,5,4); 
    elements["bar3"].default = 5; // Same as "bar1", then sets default to 5 
    return 0; 
} 
2

Como se señaló Steve Townsend, std::map es similar en concepto, pero tiene una aplicación diferente.

Creación de su contenedor anidado en C++ es un poco más detallado:

#include <tr1/unordered_map> 
#include <iostream> 

typedef std::tr1::unordered_map< std::string, int > Inner; 
typedef std::tr1::unordered_map< std::string, Inner > Outer; 

int main() 
{ 
    Outer foo; 

    Inner::value_type bar1_data[] = { 
    Inner::value_type("Default", 0), 
    Inner::value_type("Value", 0), 
    }; 
    const size_t n_bar1_data = sizeof(bar1_data)/sizeof(*bar1_data); 

    foo["bar1"] = Inner(bar1_data, bar1_data + n_bar1_data); 

    Inner::value_type bar2_data[] = { 
    Inner::value_type("Default", 2), 
    Inner::value_type("value", 5), 
    Inner::value_type("other", 4), 
    }; 
    const size_t n_bar2_data = sizeof(bar2_data)/sizeof(*bar2_data); 

    foo["bar2"] = Inner(bar2_data, bar2_data + n_bar2_data); 

como se documenta en perlref, flechas entre corchetes subíndices son opcionales, por lo que podría haber escrito (comentó para mantener el flujo del programa en C++)

// $foo{"bar1"}{"Default"} = 15; 

que es bastante cerca de C++:

foo["bar1"]["Default"] = 15; 

En buena medida es la impresión de la estructura resultante y devolver 0 de main:

for (Outer::const_iterator o = foo.begin(); o != foo.end(); ++o) { 
    std::cout << o->first << ":\n"; 
    for (Inner::const_iterator i = o->second.begin(); i != o->second.end(); ++i) 
     std::cout << " - " << i->first << " => " << i->second << '\n'; 
    } 

    return 0; 
} 

Salida:

bar1: 
    - Value => 0 
    - Default => 15 
bar2: 
    - Default => 2 
    - value => 5 
    - other => 4

NOTA: entrada y salida tienen el mismo orden en este programa de juguete, pero ¡no dependa de este comportamiento!

Si por el contrario prefiere utilizar boost::unordered_map, cambiar unas pocas líneas en la parte superior de su programa:

#include <boost/unordered_map.hpp> 

typedef boost::unordered_map< std::string, int > Inner; 
typedef boost::unordered_map< std::string, Inner > Outer; 
+1

Al no ser un gran usuario de C++, TR1 es el avance de la nueva STL, mientras que un tercer impulso es la sustitución del partido durante gran parte de la funcionalidad? –

+2

@ Evan [TR1] (http://en.wikipedia.org/wiki/C%2B%2B_Technical_Report_1) es un conjunto de propuestas de adiciones a la biblioteca estándar el C++, varios de los cuales están basados ​​en [Boost] (http://www.boost.org/) bibliotecas. Puede pensar en Boost como un campo de pruebas para adiciones futuras a la biblioteca estándar de C++. –

0

Permítanme añadir meta-información a la pregunta. Otros han proporcionado ejemplos de códigos de trabajo reales.

Además de ser un tipo diferente de contenedor (acceso O (log (n) para mapas C++ vs. O (1) para hashes Perl), la diferencia principal entre los mapas C++ y los hashes Perl radica en la estática vs. Los argumentos de plantilla de un mapa especifican claramente el tipo de clave y el valor, mientras que para los valores hash de Perl, solo la clave tiene un tipo conocido (una cadena). El valor es un escalar, pero eso podría y normalmente lo hace contener cualquier tipo de estructura de datos.Por lo tanto, es muy complicado de usar mapas C++ para una estructura de datos bastante común Perl como este:

{ 
    foo => [qw(bar baz)], 
    quibble => { 
    lion => 'animal', 
    pine => 'plant', 
    }, 
    count => 5, 
} 

Esto es más de una distinción práctico que una conceptual porque siempre se puede definir una estructura/clase/tipo eso contendrá cosas arbitrarias (ver otras respuestas). Simplemente es mucho más trabajo y termina siendo bastante engorroso de usar.

El uso de un tipo de plantilla simple como map<string, map<string, Stuff> > solo es posible si la estructura de sus datos es muy regular.

0

¿Cómo puedo hacer esto en C++ usando STL? Tal vez esta es una pregunta simple, pero no puedo resolverlo.

La forma de utilizar el hash de Perl es la manera habitual de imitar el struct de C/C++.

Por lo tanto creo que el más cercano C++ traducción de su código es:

struct foobar { 
    int Default, value, other; 

    foobar(int Default_ = 0, int value_ = 0, int other_ = 0) 
    : Default(Default_), value(value_), other(other_) 
    {} 
}; 

std::map< std::string, foobar > foo; 

foo["bar1"] = foobar(0, 0); 
foo["bar2"] = foobar(2, 5, 4); 
Cuestiones relacionadas