2012-03-21 4 views
7

Ho uno script che utilizza un hash, che contiene quattro stringhe come chiavi i cui valori sono hash. Questi hash contengono anche quattro stringhe come chiavi che hanno anche hash come valori. Questo modello continua fino ai livelli n-1, che viene determinato in fase di esecuzione. L'ennesimo livello di hash contiene valori interi (al contrario dei soliti riferimenti ad hash).Can BerkeleyDB in perl gestisce un hash di hash di hash (fino a n)?

Ho installato il modulo BerkeleyDB per Perl in modo da poter utilizzare lo spazio su disco anziché RAM per archiviare questo hash. Ho pensato che avrei potuto semplicemente legare la hash a un database, e sarebbe lavorare, così ho aggiunto il seguente al mio codice:

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


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

Tuttavia, ottengo l'errore:

Impossibile utilizzare stringa ("HASH (0x1a69ad8)") come riferimento HASH mentre "strict refs" in uso su getUniqSubTreeBDB.pl riga 31, riga 1.

Per testare, ho creato un nuovo script, con il codice (sopra) quello legato hash in un file. Poi ho aggiunto il seguente:

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

E ha funzionato bene. Poi ho aggiunto:

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

E darei più o meno lo stesso errore. Sto pensando che BerkeleyDB non possa gestire il tipo di struttura dati che sto creando. Forse è stato in grado di gestire il primo livello (C -> {}) nel mio test perché era solo una normale chiave -> scaler?

Ad ogni modo, qualsiasi suggerimento o affermazione della mia ipotesi sarebbe apprezzata.

risposta

7

Utilizzare DBM::Deep.

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

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

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

Il codice che ho fornito ieri funzionerebbe correttamente con questo.

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 /. In realtà non l'ho provato. – ikegami

+0

Ho provato questo sostituendo $ root = \% tag con $ root = tie $ tags, "DBM :: Deep", $ dbFile. Il programma viene eseguito più lentamente, ma utilizza anche la RAM? Ho pensato che se si utilizza un database la RAM non verrà utilizzata per memorizzare l'hash? – gravitas

+0

@RSinghS, L'intero punto di utilizzo di un database sarebbe evitare l'uso della memoria, non vedo perché userebbe molta memoria. – ikegami

1

No. BerkeleyDB memorizza coppie di una chiave e un valore, in cui entrambi sono arbitrari caratteri di estinzione. Se si memorizza un hashref come valore, verrà memorizzata la rappresentazione di stringa di un hashref, che non è molto utile quando lo si legge di nuovo (come notato).

Il modulo MLDBM può fare qualcosa come descritto, ma funziona serializzando l'hashref di livello superiore a una stringa e memorizzandolo nel file DBM. Ciò significa che deve leggere/scrivere l'intero hashref di primo livello ogni volta che si accede o si modifica un valore in esso.

A seconda dell'applicazione, è possibile combinare le chiavi in ​​una singola stringa e utilizzarle come chiave per il file DBM. La principale limitazione è che è difficile scorrere le chiavi di uno dei tuoi hash interiori.

Per questo è possibile utilizzare il semi-obsoleto multidimensional array emulation. $foo{$a,$b,$c} viene interpretato come $foo{join($;, $a, $b, $c)} e funziona anche con gli hash legati.

+0

Purtroppo, che non funziona con '$ pippo {} @indexes'. Il numero di indici deve essere noto al momento della compilazione. Poteva precisare il "join" come hai fatto tu, però. Il fatto è che in precedenza aveva chiesto come creare l'hash multilivello anziché utilizzare la chiave unita. – ikegami

1

No; può solo memorizzare le stringhe. Ma puoi usare lo →filter_fetch_value e lo →filter_store_valueto define "filters" che bloccherà automaticamente le strutture arbitrarie alle stringhe prima di memorizzarle e di convertirle al momento del recupero. Esistono ganci analoghi per il marshalling e le chiavi non stringhe non riarrangiate.

Attenzione però: l'utilizzo di questo metodo per archiviare oggetti che condividono sottooggetti non preserverà la condivisione. Ad esempio:

$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 qui è un hash legato; se si trattasse di un normale hash, i due print s stamperebbero entrambi 4.

1

Mentre non è possibile archiviare normali hash multidimensionali in un hash legato a BerkeleyDB, è possibile utilizzare hash multidimensionali emulati con una sintassi come $ tags {'C', 'G'}. Questo crea una singola chiave che sembra ('C'. $;. 'G')

0

Ho avuto la stessa domanda, ho trovato questo. Potrebbe essere utile anche per te.

Memorizzazione strutture dati come valori in BDB

Spesso, potremmo essere interessati a memorizzare strutture dati complesse: array, hashtables, ... cui elementi possono essere valori semplici, di riferimenti ad altre strutture di dati. Per fare questo, abbiamo bisogno di serializzare la struttura dei dati: convertirla in una stringa che può essere archiviata nel database, e può essere successivamente riconvertita nella struttura dei dati originale usando una procedura di deserializzazione.

Esistono diversi moduli perl disponibili per eseguire questo processo di serializzazione/deserializzazione. Uno dei più popolari è JSON :: XS. L'esempio seguente mostra come utilizzare questo modulo:

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

in Perl si può fare questo. Stai usando riferimenti oltre il primo livello.

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; 

Questo può essere trovato in linea a partire libro perl nel capitolo 13.