Хранение нескольких значений в одном ключе в хэше с использованием Perl - PullRequest
3 голосов
/ 03 января 2012

Я пытаюсь создать структуру данных для хранения данных, которые я извлекаю из базы данных:

$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, или мне нужно для этого создать еще один слой хеша?)

Спасибо

Ответы [ 3 ]

3 голосов
/ 03 января 2012

Ваш код, но с правильными отступами, чтобы его можно было прочитать.

$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 может содержать несколько ключей, и если вы ввели данные, как указано выше, все значения будут ссылками на хеш. E.g.:

$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 ];

или

$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

Он распечатает вашу структуру данных для вас, что позволяет очень легко увидеть, что вы делаете. Обратите внимание на использование скобок и фигурных скобок для обозначения массивов и хэшей.

2 голосов
/ 03 января 2012

У вас почти было то, что вы пытались вставить в arrayref. Проблема заключалась в том, что вы уже присвоили хэш-ссылку на $Interaction[$TrGene}, а затем попытались использовать его как arrayref с @{ $Interaction{$TrGene} }.

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

  • Вы принимаете хеш-значение $Interaction{$TrGene}
  • , который вы затем разыменовываете в массив @{ ... }. Например. Вы можете сделать это: @array = @{$Interaction{$TrGene}}.
  • Если в $Interaction{$TrGene} не было никакого значения, то в этой точке автоматически будет создан arrayref (известный как авто-вивификация).

Итак, предположим, что вы создали эти хэш-функции:

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

Вы можете вставить каждый из них в свой массив:

push @{ $Interaction{$TrGene} }, $CisGene1, $CisGene2;
2 голосов
/ 03 января 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.

...