使用Perl在哈希表中的哈希中将多个值存储在一个键中

3

我正在尝试创建一个数据结构来存储我从数据库中获取的数据:

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

一个$TrGene与多个CisGenes相关联(每个CisGene都有唯一的E&Q)。例如:

TrGene1 CisGene1 Q1 E2

TrGene1 CisGene2 Q2 E3

最后一个TrGene1会覆盖之前的TrGene1。我认为我需要创建对数组的引用,但在阅读了这个网页之后,我还不太理解应该如何做:http://perldoc.perl.org/perlreftut.html

我尝试使用该网页上的国家/城市示例,但并不成功:

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

我遇到了一个“不是数组引用”的错误。 我仅使用了$CisGene,但它不能覆盖该CisGene的E和Q值。(所以这个哈希表是否知道CisGene与特定的E和Q相关联,还是我需要为此创建另一层哈希表?)

这个链接可能对你有所帮助:http://htmlfixit.com/cgi-tutes/tutorial_Perl_Primer_013_Advanced_data_constructs_An_array_of_hashes.php - run
3个回答

3

您的代码,但是正确缩进以便更易读。

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

代码解释:

你使用花括号{}为匿名哈希分配一个键值对列表,并将该哈希引用分配给%Interaction哈希中的$TrGene键。然后你尝试使用@{ ... }将该值作为数组引用,但这是不起作用的。

如果你使用不同的值输入哈希键,则会覆盖它们。让我们看一些实际的例子,这真的很容易

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

Rewritten:

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

它会为您打印出数据结构,这使得很容易看到您在做什么。请注意它使用方括号和花括号来表示数组和哈希表。


非常感谢您抽出时间向我解释这个问题!现在我理解得更清楚了,同时Data::Dumper模块也非常有用,将节省我很多时间。 - Lisa
我该如何访问每个元素?我已经编写了以下变体: foreach my $TrGene (keys %Interaction) { my(@CisGene) = @{$Interaction{$TrGene}}; print "[$TrGene] [@CisGene]\n"; 但这只打印了TrGene而没有cisgene(我不是想打印数据,我想使用它(Q和E实际上是数字而不是字符串,我需要在方程中使用它们),所以不能只使用Data :: Dumper,现在只是为了检查循环是否正常工作而打印)。 - Lisa
@Lisa,我在这里的最后一部分犯了一个错误,现已更正。我将添加有关访问元素的说明。 - TLP

2

Something like

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

应该可以工作。

$Interaction{$TrGene} 不能是数组引用,因为你刚刚分配了一个哈希引用。

当然,在分配之前,如果你想要 EQ 的组合(我假设这在 $TrGene 键中已经表示出来了,否则,你可能会制造更多的麻烦。)你需要像这样:

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

如果EQ依赖于$TrGene的值,那么你将得到所需的分组。否则,你可以将它们视为下标,如下所示:

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

获取一个映射,使得EQ$CisGene之间的关联更强。


2

你在尝试将值推入一个数组引用时,就差一点就做到了。问题在于你已经将哈希引用分配给了$Interaction[$TrGene},然后尝试使用它作为数组引用来使用@{ $Interaction{$TrGene} }

@{ $Interaction{$TrGene} }的意思是:

  • 你正在获取哈希值$Interaction{$TrGene}
  • 然后对其进行解引用以得到一个数组@{ ... }。例如,你可以这样做:@array = @{$Interaction{$TrGene}}
  • 如果$Interaction{$TrGene}中没有值,则数组引用将在此时自动创建(称为自动vivication)。

所以,假设你已经创建了这些哈希引用:

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

你可以将它们中的每一个推入你的数组引用中:
push @{ $Interaction{$TrGene} }, $CisGene1, $CisGene2;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接