2012-03-21 23 views
7

Tengo un script que utiliza un hash, que contiene cuatro cadenas como claves cuyos valores son hashes. Estos hash también contienen cuatro cadenas como claves que también tienen hashes como sus valores. Este patrón continúa hasta n-1 niveles, que se determina en tiempo de ejecución. El n-ésimo nivel de hash contiene valores enteros (en oposición a la referencia hash habitual).¿Puede BerkeleyDB en perl manejar un hash de hashes de hashes (hasta n)?

Instalé el módulo BerkeleyDB para Perl, así que puedo usar espacio en disco en lugar de RAM para almacenar este hash. Supuse que yo podría simplemente atar el hash a una base de datos, y que iba a funcionar, por lo que añade lo siguiente a mi código:

my %tags =() ; 
my $file = "db_tags.db" ; 
unlink $file; 


tie %tags, "BerkeleyDB::Hash", 
     -Filename => $file, 
     -Flags => DB_CREATE 
    or die "Cannot open $file\n" ; 

Sin embargo, me sale el error:

No se puede utilizar cuerdas ("HASH (0x1a69ad8)") como un HASH ref mientras "strict refs" en uso en getUniqSubTreeBDB.pl línea 31, línea 1.

Para probar, creé un nuevo script, con el código (arriba) vinculado al hash a un archivo. Luego agregué lo siguiente:

my $href = \%tags; 
$tags{'C'} = {} ; 

Y funcionó bien. Luego agregué:

$tags{'C'}->{'G'} = {} ; 

Y daría casi el mismo error. Estoy pensando que BerkeleyDB no puede manejar el tipo de estructura de datos que estoy creando. Tal vez fue capaz de manejar el primer nivel (C -> {}) en mi prueba porque era solo una clave normal -> scaler?

De todos modos, cualquier sugerencia o afirmación de mi hipótesis sería apreciada.

Respuesta

7

Use DBM::Deep.

my $db = DBM::Deep->new("foo.db"); 

$db->{mykey} = "myvalue"; 
$db->{myhash} = {}; 
$db->{myhash}->{subkey} = "subvalue"; 

print $db->{myhash}->{subkey} . "\n"; 

El código que proporcioné ayer funcionaría bien con esto.

sub get_node { 
    my $p = \shift; 
    $p = \(($$p)->{$_}) for @_; 
    return $p; 
} 

my @seqs = qw(CG CA TT CG); 

my $tree = DBM::Deep->new("foo.db"); 
++${ get_node($tree, split //) } for @seqs; 
+0

s/would/should /. Realmente no lo probé. – ikegami

+0

Intenté esto reemplazando las etiquetas $ root = \% con $ root = etiquetas de unión $, "DBM :: Deep", $ dbFile. El programa se ejecuta más lentamente, pero también está usando RAM? Pensé que si utilizabas una base de datos, ¿tu RAM no se usaría para almacenar el hash? – gravitas

+0

@RSinghS, El objetivo de usar una base de datos sería evitar el uso de la memoria, no veo por qué usaría mucha memoria. – ikegami

1

No. BerkeleyDB almacena pares de una clave y un valor, donde ambas son cadenas de bytes arbitrarias. Si almacena un hashref como valor, almacenará la representación de cadena de un hashref, que no es muy útil cuando lo lee de nuevo (como notó).

El módulo MLDBM puede hacer algo como usted lo describe, pero funciona serializando el hashref de nivel superior en una cadena y almacenándolo en el archivo DBM. Esto significa que tiene que leer/escribir todo el hashref de nivel superior cada vez que accede o cambia un valor en él.

Dependiendo de su aplicación, puede combinar sus claves en una sola cadena y usarlas como la clave para su archivo DBM. La principal limitación de eso es que es difícil iterar sobre las teclas de uno de tus hashes interiores.

Puede usar el multidimensional array emulation semi obsoleto para esto. $foo{$a,$b,$c} se interpreta como $foo{join($;, $a, $b, $c)}, y eso también funciona con hashes atados.

+0

Por desgracia, que no funciona con '$ foo {}' @indexes. La cantidad de índices debe conocerse en tiempo de compilación. Sin embargo, podía deletrear la 'unión' como lo hiciste tú. Lo que pasa es que previamente preguntó cómo crear el hash multinivel en lugar de usar la clave unida. – ikegami

1

No; solo puede almacenar cadenas. Pero puede usar →filter_fetch_value y →filter_store_valueto define "filters" que congelarán automáticamente las estructuras arbitrarias en las cadenas antes de almacenarlas, y convertirlas de nuevo al recuperarlas. Hay ganchos análogos para ordenar y desasignar claves que no son de cadena.

Sin embargo, tenga cuidado: el uso de este método para almacenar objetos que comparten subobjetos no preservará el uso compartido. Por ejemplo:

$a = [1, 2, 3]; 
$g = { array => $a }; 
$h = { array => $a }; 
$db{g} = $g; 
$db{h} = $h; 

@$a =(); 
push @{$db{g}{array}}, 4; 

print @{$db{g}{array}}; # prints 1234, not 4 
print @{$db{h}{array}}; # prints 123, not 1234 or 4 

%db aquí está un hash vinculado; si fuera un hash ordinario, los dos print s imprimirían 4.

1

Si bien no puede almacenar hashes multidimensionales normales en un hash vinculado BerkeleyDB, puede usar hashes multidimensionales emulados con una sintaxis como $ etiquetas {'C', 'G'}. Esto crea una sola clave que se ve como ('C'. $;. 'G')

0

Tuve la misma pregunta, encontré esto. Puede ser útil para usted también.

Almacenamiento de estructuras de datos como valores en BDB

A menudo, podemos estar interesados ​​en almacenar estructuras de datos complejas: matrices, tablas hash, ... cuyos elementos pueden ser valores simples, de referencias a otras estructuras de datos. Para hacer esto, necesitamos serializar la estructura de datos: conviértala en una cadena que se pueda almacenar en la base de datos, y luego se puede volver a convertir a la estructura de datos original usando un procedimiento de deserialización.

Existen varios módulos perl disponibles para realizar este proceso de serialización/deserialización. Uno de los más populares es JSON :: XS. El siguiente ejemplo muestra cómo usar este módulo:

use JSON::XS; 

# Data to be stored 
my %structure; 

# Convert the data into a json string 
my $json = encode_json(%structure); 

# Save it in the database 
$dbh->db_put($key,$json); 
To retrieve the original structure, we perform the inverse operation: 

# Retrieve the json string from the database 
$dbh->db_get($key, $json); 

# Deserialize the json string into a data structure 
my $hr_structure = decode_json($json); 
0

En Perl puede hacerlo. Está utilizando referencias más allá del primer nivel.

use GDBM_File; 
use Storable; 
use MLDBM qw(GDBM_File Storable); 
my %hash; 
my %level_2_hash; 
my %level_3_hash1 = (key1 => x, key2 => y, key3 => z) 
my %level_3_hash2 = (key10 => a, key20 => b, key30 => c) 
$level_2_hash = (keyA => /%level_3_hash1, keyB => level_3_hash2) 
$hash{key} = \%level_2_hash; 

esto se puede encontrar en el libro en línea a partir del Perl en el capítulo 13.