Съхраняване на множество стойности в един ключ в хеш на хеш с помощта на Perl

Опитвам се да създам структура от данни за съхраняване на данни, които извличам от база данни:

$Interaction{$TrGene}={
CisGene => $CisGene,
E => $e,
Q => $q,};

Единичен $TrGene е свързан с редица CisGenes (който има уникален E & Q). например:

TrGene1 CisGene1 Q1 E2

TrGene1 CisGene2 Q2 E3

Последният TrGene1 презаписва тези, които са били преди него. Мисля, че трябва да създам препратка към масив, но не разбирам напълно как трябва да стане това, след като прочетох тази уеб страница: http://perldoc.perl.org/perlreftut.html

Опитах се да използвам примера за държава/град на тази уеб страница, но не успях:

$Interaction{$TrGene}={
CisGene => $CisGene,
E => $e,
Q => $q,};
push @{$Interaction{$TrGene}}, $CisGene;

Получавам грешка „Не е ARRAY ref“. Също така съм използвал само $CisGene там, но трябва да не презаписва E & Q стойностите за този CisGene. (така че този хеш ще знае, че CisGene е свързан с конкретни E и Q, или трябва да създам друг слой хеш за това?)

Благодаря


person Lisa    schedule 03.01.2012    source източник
comment
htmlfixit.com/cgi-tutes/ това може да ви помогне   -  person run    schedule 03.01.2012


Отговори (3)


Вашият код, но с правилен отстъп, така че да е четим.

$Interaction{$TrGene} = {
    CisGene => $CisGene,
    E       => $e,
    Q       => $q,
};
push @{$Interaction{$TrGene}}, $CisGene;

Кодът е обяснен:

Присвоявате списък от двойки ключ-стойност на анонимен хеш, като използвате къдрави скоби {}, и присвоявате тази препратка към хеш към ключа $TrGene в %Interaction хеша. След това се опитвате да използвате тази стойност като препратка към масив, като я ограждате с @{ ... }, което не работи.

Ако въведете хеш ключ с различни стойности, ще ги презапишете. Нека вземем няколко практически примера, наистина е доста лесно.

$Interaction{'foobar'} = {
    CisGene => 'Secret code',
    E       => 'xxx',
    Q       => 'yyy',
};

Сега сте съхранили препратка към хеш под ключ 'foobar'. Този хеш всъщност е отделна препратка към структура от данни. Мисля, че е по-лесно да следите структурите, ако мислите за тях като скалари: хеш (или масив) може да съдържа само скалари.

Хешът %Interaction може да съдържа няколко ключове и ако сте въвели данни като по-горе, всички стойности ще бъдат препратки към хеш. напр.:

$hash1 = {  # note: curly brackets denote an anonymous hash 
    CisGene => 'Secret code',
    E       => 'xxx',
    Q       => 'yyy',
};
$hash2 = {
    CisGene => 'some other value',
    E       => 'foo',
    Q       => 'bar',
};

%Interaction = ( # note: regular parenthesis denote a list
    'foobar'   => $hash1,  # e.g. CisGene => 'Secret code', ... etc. from above
    'barbar'   => $hash2   # e.g. other key value pairs surrounded by {}
    ...
);

Типът стойност, съдържащ се в $hash1 и $hash2, сега е препратка, адрес към данни в паметта. Ако го отпечатате print $hash1, ще видите нещо като HASH(0x398a64).

Сега, ако въведете нова стойност в %Interaction, като използвате съществуващ ключ, този ключ ще бъде презаписан. Тъй като един хеш ключ може да съдържа само една стойност: скалар. В нашия случай препратка към хеш.

Това, което се опитвате да направите във вашия пример, е да използвате стойността на ключа 'foobar' като препратка към масив (което е глупаво, защото, както сега можете да видите по-горе, това е препратка към хеш):

push @{$Interaction{$TrGene}}, $CisGene;

Пренаписано:

push @{  $hash1  }, 'Secret code';  # using the sample values from above

Не... това не работи.

Това, от което се нуждаете, е нов контейнер. Вместо това ще направим стойността на ключа 'foobar' препратка към масив:

%Interaction = (
    'foobar'   => $array1,
    ...
);

Където:

$array1 = [ $hash1, $hash2 ];

or

$array1 = [       # note the square brackets to create anonymous array
              {   # curly brackets for anonymous hash
                  CisGene => 'Secret code',
                  E       => 'xxx',
                  Q       => 'yyy',
              },  # comma sign to separate array elements
              {   # start a new hash
                  CisGene => 'Some other value',
                  E       => 'foo',
                  Q       => 'bar',
              }   # end 
           ];     # end of $array1

Всичко това е доста тромав начин за поставяне на нещата, така че нека го опростим:

$CisGene = 'foobar';
$e = 'xxx';
$q = 'yyy';

my $hash1 = {
        CisGene => $CisGene,
        E       => $e,
        Q       => $q,
};

push @{$Interaction{$TrGene}}, $hash1;

Или можете да премахнете временната променлива $hash1 и да я присвоите директно:

push @{$Interaction{$TrGene}}, {
    CisGene => $CisGene,
    E       => $e,
    Q       => $q,
};

И при достъп до елементите:

for my $key (keys %Interaction) {  # lists the $TrGene keys 
    my $aref = $Interaction{$key}; # the array reference
    for my $hashref (@$aref) {     # extract hash references, e.g. $hash1
        my $CisGene = $hashref->{'CisGene'};
        my $e       = $hashref->{'E'};
        my $q       = $hashref->{'Q'};
    }
}

Обърнете внимание на използването на оператора стрелка, когато работите директно с препратки. Можете също да кажете $$hashref{'CisGene'}.

Или директно:

my $CisGene = $Interaction{'foobar'}[0]{'CisGene'};

Препоръчвам да прочетете perldata. Много удобен модул е ​​Data::Dumper. Ако го направиш:

use Data::Dumper;
print Dumper \%Interaction; # note the backslash, Dumper wants references

Той ще отпечата структурата на вашите данни вместо вас, което прави много лесно да видите какво правите. Обърнете внимание на използването на скоби и къдрави скоби за обозначаване на масиви и хешове.

person TLP    schedule 03.01.2012
comment
Благодаря много, че отделихте време да ми обясните това! Сега има много повече смисъл, а също и модулът Data::Dumper е много полезен, ще ми спести много време. - person Lisa; 03.01.2012
comment
как да получа достъп до всеки елемент? Написал съм варианти на: foreach my $TrGene (ключове %Interaction) { my(@CisGene) = @{$Interaction{$TrGene}}; печат [$TrGene] [@CisGene]\n; Но това отпечатва само TrGene, а не cisgene (не че искам да отпечатам данните, искам да ги използвам (Q & E всъщност са числа, а не низове, които трябва да използвам в уравнение), така че не мога просто използвайте Data::Dumper, засега само отпечатвам, за да проверя дали циклите работят) - person Lisa; 03.01.2012
comment
@Lisa Допуснах грешка в последната част тук, която сега е коригирана. Ще добавя обяснение за достъпа до елементите. - person TLP; 04.01.2012

Нещо като

push @{ $Interaction{ $TrGene }{members} }, $CisGene;

трябва да работи.

$Interaction{$TrGene} не може да бъде препратка към масив, тъй като току-що сте му присвоили препратка към хеш.

Разбира се, първо трябва да проверите, преди да присвоите, ако искате комбинация от E и Q (ще предположа, че е посочено в клавиша $TrGene или в противен случай вероятно създавате повече бъркотия. ), ще искате нещо по-подобно на това:

 $Interaction{ $TrGene } //= { E => $e, Q => $q };
 push @{ $Interaction{ $TrGene }{CisGenes} }, $CisGene;

По този начин, ако E и Q зависят от стойността на $TrGene, ще получите групиранията, от които се нуждаете. В противен случай можете да ги считате за индекси, както следва:

push @{ $Interaction{ $e }{ $q } }, $CisGene;

и да получите съпоставяне с по-голяма връзка между E, Q и $CisGene.

person Axeman    schedule 03.01.2012

Почти го имахте, когато се опитахте да вкарате arrayref. Проблемът беше, че вече сте присвоили hashref на $Interaction[$TrGene}, след което сте се опитали да го използвате като arrayref с @{ $Interaction{$TrGene} }.

@{ $Interaction{$TrGene} } означава:

  • Вие приемате хеш стойността $Interaction{$TrGene}
  • който след това дереферирате към масив @{ ... }. напр. можете да направите това: @array = @{$Interaction{$TrGene}}.
  • Ако нямаше стойност в $Interaction{$TrGene}, тогава arrayref ще бъде създаден автоматично в тази точка (известно като автоматично оживяване).

И така, ако приемем, че сте създали тези hashrefs:

my $CisGene1 = {
    CisGene => 'CisGene1',
    E => 'E1',
    Q => 'Q1',
};
my $CisGene2 = {
    CisGene => 'CisGene2',
    E => 'E3',
    Q => 'Q2',
};

Можете да поставите всеки от тях във вашия arrayref:

push @{ $Interaction{$TrGene} }, $CisGene1, $CisGene2;
person stevenl    schedule 03.01.2012