【问题标题】:Looping through known elements in a hash of hashes of arrays遍历数组散列中的已知元素
【发布时间】:2013-04-11 09:03:46
【问题描述】:

我有一个问题希望有人能提供帮助(为了解释我的问题而进行了简化)。

我有以下数组散列的散列(我认为它就是这样?)

数据结构

{
  Cat => {
    Height => ["Tiny"],
  },
  Dog => {
    Colour => ["Black"],
    Height => ["Tall"],
    Weight => ["Fat", "Huge"],
  },
  Elephant => {
    Colour => ["Grey"],
    Height => ["Really Big"],
    Weight => ["Fat", "Medium", "Thin"],
  },
}

我想做什么

下面的程序将打印整个数据结构。 我想用这种方式来做

my %h;

for my $animal (keys %h) {
   print "$animal\n";
   for my $attribute ( keys %{$h{$animal}} ) {
        print "\t $attribute\n";
        for my $i (0 .. $#{$h{$animal}{$attribute}} ) {
            print "\t\t$h{$animal}{$attribute}[$i]\n";
        }    
   }   
}          

我遇到的问题

我正在尝试访问数据结构的特定部分。例如,我只想为每个动物打印出Height 数组,因为在这个例子中我不关心其他ColourWeight 属性。

我确信有一个简单的答案,我知道我需要指定 Height 部分,但正确的做法是什么?我尝试了多种我认为不会成功的方法。

【问题讨论】:

    标签: arrays perl loops hash nested


    【解决方案1】:

    在您的代码中,而不是循环遍历 所有 属性与

    for my $attribute ( keys %{ $h{$animal} } ) { ... }
    

    只用你感兴趣的。像这样

    for my $animal (keys %h) {
       print "$animal\n";
       for my $attribute ( 'Height' ) {
            print "\t $attribute\n";
            for my $i (0 .. $#{$h{$animal}{$attribute}} ) {
                print "\t$h{$animal}{$attribute}[$i]\n";
            }    
       }   
    }          
    

    我会选择循环遍历 heights 数组的 contents 而不是索引,使代码看起来像这样:

    for my $animal (keys %h) {
        print "$animal\n";
        print "\t\t$_\n" for @{ $h{$animal}{Height} };
    }          
    

    【讨论】:

    • 我想我可能刚刚解决了同样的问题 - 我的答案中显示的方法也是一种可接受的方法吗? - 非常感谢
    • 是不是因为你没有像我在回答中那样将Height 属性“硬编码”到循环中?
    • 好吧,我根本不会这样编码。在您拒绝 Miguel 的回答后,我写了这篇文章,因为它与您原来的答案太不一样了。我的解决方案尽可能地改变了最小的可能性,而您自己的解决方案与您以前的解决方案相比,变化更大。很难准确说出您想要保留原始代码的哪些方面。
    • 当你说你“根本不会这样编码”时,这是你个人喜好和你喜欢的方式问题,还是这不是最好/首选的方法去做这件事?谢谢
    • 两者兼而有之。您使用表达式$h{$animal}{$attribute} 两次,这通常被认为是不好的做法。当您不需要索引时,您可以遍历 heights 数组的索引,除了访问这些值。我已经为我的答案添加了我对解决方案的看法。希望你会同意我的看法,这是一种改进。
    【解决方案2】:

    快速查看您的数据结构:它是散列的散列数组!哇。思想正式吹响了。

    这是打印所有数据的快速方法:

    use feature qw(say);
    
    # Working with a Hash of Hash of Arrays
    for my $animal (keys %h) {
        say "Animal: $animal";
        # Dereference: Now I am talking about a hash of arrays
        my %animal_attributes = %{ $h{$animal} };
        for my $attribute (keys %animal_attributes) {
            # Dereference: Now I am talking about just an array
            my @attribute_value_list = @{ $animal_attributes{$attribute} };
            say "\tAttribute: $attribute - " . join ", ", @attribute_value_list;
        }
    }
    

    注意我使用解引用。我没有 来进行取消引用,但它使代码更易于使用。我不必考虑我的各种水平。我知道我的动物是属性的 hash,而这些属性是属性值的 array。通过使用取消引用,它可以让我保持直截了当。

    现在,假设您只想打印出所需属性的列表。您可以在尝试打印之前使用exists 函数查看该属性是否存在。

    use feature qw(say);
    use constant DESIRED_ATTRIBUTES => qw(weight height sex_appeal);
    
    # Working with a Hash of Hash of Arrays
    for my $animal (keys %h) {
        say "Animal: $animal";
        # Dereference: Now I am talking about a hash of arrays
        my %animal_attributes = %{ $h{$animal} };
        for my $attribute ( DESIRED_ATTRIBUTES ) {
            if ( exists $animal_attributes{$attribute} ) {
                # Dereference: Now I am talking about just an array
                my @attribute_value_list = @{ $animal_attributes{$attribute} }; 
                say "\tAttribute: $attribute - " . join ", ", @attribute_value_list;
            }
        }
    }
    

    同样的代码,我只是添加了一个 if 子句。

    当您了解这些复杂的数据结构时,使用面向对象设计可能会更好。 Perl 在 OOP Perl 上有一个 excellent tutorial。如果你使用它,你可以定义一类动物,并有各种方法来提取你想要的数据。它使维护变得更加容易,并允许您勇敢地创建更复杂的数据结构,而无需担心跟踪您的位置。

    【讨论】:

    • 谢谢,解引用解释/cmets 也很有帮助
    【解决方案3】:

    我认为有时直接使用该值会更容易,如果它是对另一个结构的引用。你可以这样做:

    my $height = "Height";
    
    while (my ($animal, $attr) = each %h) {
        print "$animal\n";
        print "\t$height\n";
        print "\t\t$_\n" for @{ $attr->{$height} };
    }
    

    使用主键的值,您可以跳过引用的一步,直接进入Height 属性。下面的输出是您在原始代码中的格式。

    输出:

    Elephant
            Height
                    Really Big
    Cat
            Height
                    Tiny
    Dog
            Height
                    Tall
    

    【讨论】:

      【解决方案4】:

      假设你的变量被称为 %h:

      foreach my $animal (keys %h) {
         my $heights = $h{$animal}->{Height}; #gets the Height array
         print $animal, "\n";
         foreach my $height( @$heights ) {
            print "  ", $height, "\n";
         }
      }
      

      【讨论】:

      • 谢谢,如何使用我的问题中显示的foreach 循环来完成?谢谢。
      • 不必要地复制整个 @heights 数组。保留参考,例如my $heights = $h{$animal}{Height} 会更好。
      【解决方案5】:

      我想我已经解决了,发现我做错了什么?

      我认为应该是这样的:

      my %h;
      
      for my $animal (keys %h) {
            print "$animal\n";
            for my $i (0 .. $#{$h{$animal}{Height}} ) {
                print "\t\t$h{$animal}{Height}[$i]\n";
            }    
      }   
      

      【讨论】:

        猜你喜欢
        • 2018-06-29
        • 2023-04-10
        • 2019-01-31
        • 2012-08-03
        • 2012-09-16
        • 2012-12-03
        • 2016-08-03
        • 2021-08-12
        • 1970-01-01
        相关资源
        最近更新 更多