【问题标题】:How to correctly dereference a hash within a foreach loop?如何在 foreach 循环中正确取消引用哈希?
【发布时间】:2015-06-07 04:25:44
【问题描述】:

我想打印我通过以下方式创建的哈希的内容: $dataStore{"$items1"}{"$items2"}{"$items3"}{'counts'}[0] = 534;

编辑:我忘了补充说上面的变量是在 while 循环内从文件中读取的,但我把它省略了,因为根据 Data::Dumper 输出可以正常工作。

基本上,我正在创建一个以数组结尾的嵌套哈希结构。我的程序正在使用上述方法将值输入到数组的不同位置。

使用 Data::Dumper 我可以看到它工作正常。

$VAR1 = {
    'items1' => {
        'items2' => {
            'items3' => {
                'counts' => [
                    '534'
                ]
...

但是,问题是我想使用 foreach 循环打印结构。主要是因为这是我知道的唯一方法。

我的 foreach 循环看起来像这样,还没有太多格式:

foreach my $item1 (sort keys %dataStore) {
    print "$item1\n";
    foreach my $item2 (sort keys $dataStore{"$item1"}) {
        print "%$item2\n";
        foreach my $item3 (sort keys $dataStore{"$item1"}{"$item2"}) {
            print "%$item3\n";
            print join (', ', @{$dataStore{"$item1"}{"$item2"}{"$item3"}})
        }
    }
}

结果最终看起来像这样(我已将数据匿名化):

item1
item2
item3
HASH(0x2c10dc0)item3
HASH(0x2b1dc70)item3
HASH(0x295bbd8)item3
HASH(0x2c34fb8)item3
HASH(0x2cca6a0)item3
HASH(0x2ba5ef0)item3
HASH(0x28fcb58)item3
HASH(0x2cca760)item3

根据我目前所读到的内容,我认为我的问题是我错误地取消了对哈希的引用,但我不确定。它可能就像在错误的事情上调用 print 一样简单。我尝试了许多不同的方法来取消引用哈希,但我要么得到这样的结果,要么在 perl 抱怨在 foreach 循环中排序引用是“实验性的”之后截断结果。

我通过 cygwin 在 Windows 8.1 上使用 Perl v5.20.1。

谢谢。

【问题讨论】:

  • 您发布的代码不会产生该输出。在use strict下也是一个错误。你也应该use warnings。发布一个工作示例来演示您遇到的问题。
  • 顺便说一句,如果字符串只是一个变量,则不需要引号。在任何地方都是如此,而不仅仅是哈希键。此外,如果键是字母数字常量,则不需要引号。 $dataStore{$items1}{$items2}{$items3}{counts}[0] = 534;每个人都有自己的喜好,但是如果您阅读其他人的代码,您可能会遇到。

标签: perl hash


【解决方案1】:

我认为更好的方法是使用reference 来代替这种方式。如果您的哈希值增加,则需要一次又一次地更改此代码。同样在某个时候,在这段代码中维护和编写更多内容几乎是不可能的。

您可以使用此代码解决您的问题。此外,这可能没有太多优化,因此您可能需要这样做。但它会解决你的问题。

use strict;
use warnings;     
my $dataStore =  {
    'items1' => {
        'items2' => {
            'items3' => {
                'counts' => [
                    '534'
                ]
}}}};


my $temp_hash = $dataStore;
my $key;
my $flag=0;
while (1) {
foreach  $key ( keys %$temp_hash) {

        if (ref($temp_hash->{$key}) eq 'HASH') {
                $temp_hash=$temp_hash->{$key};
        }

        elsif (ref($temp_hash->{$key}) eq 'ARRAY') {
                my $arr = $temp_hash->{$key};
                print join (',', @$arr);
                $flag =1;

        }
}
last if $flag ==1 ;
}

希望这能解决你的问题。

【讨论】:

  • 谢谢,我将另一个回复标记为答案,因为它帮助我在原始上下文中更好地理解了我的问题,但我会考虑您的建议!
【解决方案2】:

确保use strictuse warnings,避免实验性自动取消引用是个好主意。您的代码中有几个运行时错误,您可能会在严格模式下捕获,例如,您尝试将 items3 取消引用到一个数组中,但它是一个 hashref,键 'counts' 然后是一个 arrayref。

给定:

my $items1 = "items1";
my $items2 = "items2";
my $items3 = "items3";
my %dataStore = ();
$dataStore{"$items1"}{"$items2"}{"$items3"}{'counts'}[0] = 534;

然后:

foreach my $item1 (sort keys %dataStore) {
    foreach my $item2 (sort keys %{$dataStore{"$item1"}}) {
        print "%$item2\n";
        foreach my $item3 (sort keys %{$dataStore{"$item1"}{"$item2"}}) {
            print "%$item3\n";
            print join (', ', @{$dataStore{"$item1"}{"$item2"}{"$item3"}{'counts'}});
        }
    }
}

生产:

%items2
%items3
534

【讨论】:

  • 谢谢,我正在使用 strict 但它只会抛出警告。有了上面的更正,现在我明白了我做了什么。有趣的是,我尝试了每种方法的一些变体——但我分别尝试了它们,所以没有任何效果。谢谢。
【解决方案3】:

这是一种使用递归子程序的方法:

#!/usr/bin/perl

use warnings;
use strict;

my $h = {'i1' => {'i2' => {'i3' => [534, 10, 99]}}};

sub deref {

    my $param = shift;

    for my $k (keys(%$param)){
        if (ref($param->{$k}) eq 'HASH'){
            print "$param->{$k}\n";

            # call this sub again with the next level of the hash
            # as a parameter, if it's another hashref

            deref($param->{$k});
        }
        elsif (ref($param->{$k}) eq 'ARRAY'){
            print "$_\n" for @{ $param->{$k} };
        }
    }
}

deref($h);

【讨论】:

    猜你喜欢
    • 2015-07-04
    • 2014-05-08
    • 2012-03-05
    • 1970-01-01
    • 2016-12-04
    • 2015-05-07
    • 1970-01-01
    • 2013-01-30
    • 1970-01-01
    相关资源
    最近更新 更多