【问题标题】:Adding each element in an array to a complex hash in Perl将数组中的每个元素添加到 Perl 中的复杂散列中
【发布时间】:2015-04-07 16:57:28
【问题描述】:

我有一个包含 n 个元素的数组。我想将每个元素添加到一个复杂的哈希中,每个元素都作为一个键/值对。如果元素的数量是固定的,比如三个,我会这样做:

my %hash;
my @array = ("first", "second", "third");
$hash{$array[0]}{$array[1]}{$array[2]}++;

我想最终得到的结构是这样的(用 Data::Dumper 打印):

$VAR1 = 'first';
$VAR2 = {
        'second' => {
                   'third' => 1
};

但是当数组中的元素数量不固定时,我无法实现相同的结构。带有匿名变量并遍历数组的东西,是的,但是像

foreach @array{$hash{$_}++};
显然每个元素只会输入一个条目,而不是所需的结构。帮助?

【问题讨论】:

    标签: perl


    【解决方案1】:

    这样的东西可以为 N 个元素构建你想要的结构:

    use strict;
    use warnings; 
    
    use Data::Dumper;
    my @array = qw(first second third four five six seven);
    
    my $hash;
    foreach my $key ( reverse @array ) {
       $hash = { $key => $hash };
    }
    print Dumper $hash;
    
    __END__
    $VAR1 = {
          'first' => {
                       'second' => {
                                     'third' => {
                                                  'fourth' => {
                                                              'fifth' => {
                                                                          'sixth' => {
                                                                                     'seventh' => undef
                                                                                   }
                                                                        }
                                                            }
                                                }
                                   }
                     }
        };
    

    不清楚你真正需要这个做什么。如果您多解释一下您的用例,可能会有更好的解决方案。增加这个结构似乎并不容易。

    玩了一会儿之后,您可以通过遍历哈希引用到底部然后递增最后一个元素的值来递增。虽然不是很漂亮:|

    # incrementing
    my $elem = $hash; # copy the reference 
    foreach my $key ( @array ) {
       # found the bottom of the hash
       unless ( $elem->{$key} && ref($elem->{$key}) ) { 
          $elem->{$key}++;
          last; 
       }
    
       # not at the bottom, move to the next level
       $elem = $elem->{$key};
    }
    print Dumper $hash;
    
    __END__
    $VAR1 = {
          'first' => {
                       'second' => {
                                     'third' => {
                                                  'fourth' => {
                                                                'fifth' => {
                                                                             'sixth' => {
                                                                                          'seventh' => 1
                                                                                        }
                                                                           }
                                                              }
                                                }
                                   }
                     }
        };
    

    【讨论】:

    • 我的目标是计算文本中 ngram 的出现次数。例如,在“under a rock, and under a tree”这句话中,两个紧随其后的单词的组合将是“under a, a rock, rock and, and under, under a, a tree”。然后我想要每两个(或三个,或四个)单词组合的频率。在那句话中,所有所谓的二元组的值为 1(出现次数),而“a rock”的值为 2,因为它出现了两次。 Text::Ngrams 可能会起作用,但我对它背后的理论很感兴趣。
    • 如果您想以这种方式节省空间,这很好,但我不确定执行计数的好方法。不久前我写了一个简单的 n-gram 生成器,它可能会提供一个不错的例子:github.com/mcmillhj/markov-perl/Chain.pm
    • @vetinari:如果这是您的目标,那么您不能使用像这样的简单嵌套哈希。您需要 $hash{under}{a} 拥有自己的计数,以及作为另一个哈希引用,以便您可以在 $hash{under}{a}{rock} 中进行计数
    【解决方案2】:

    如果您维护 当前哈希 引用,这相对简单。这个小程序演示了

    前几个步骤确保每个散列元素都存在并且它的值是一个散列引用。 $href 在每个阶段移动到下一个哈希级别。对于数组的最后一个元素,最新的哈希级别的元素是递增的,而不是设置为哈希引用。

    这个数据结构是否是正确的选择取决于你在构建它之后还需要做什么

    use strict;
    use warnings;
    
    my %hash;
    my @array = qw/ first second third fourth fifth /;
    
    drill_hash(\%hash, @array);
    
    use Data::Dump;
    dd \%hash;
    
    sub drill_hash {
      my ($href, @list) = @_;
      my $final = pop @list;
      $href = $href->{$_} //= {} for @list;
      ++$href->{$final};
    }
    

    输出

    {
      first => { second => { third => { fourth => { fifth => 1 } } } },
    }
    

    更新

    了解您的目的后,保持此类 ngram 出现次数的最简单方法是拥有一个特定的哈希键,用于保持到目前为止单词序列的 count

    该程序使用值_COUNT 作为该键,例如,您可以看到{under}{a}{_COUNT}{under}{a}{rock}{_COUNT} 都是1

    use strict;
    use warnings;
    
    my %counts;
    
    count_ngram(\%counts, qw/ under a /);
    count_ngram(\%counts, qw/ a rock /);
    count_ngram(\%counts, qw/ under a rock /);
    count_ngram(\%counts, qw/ a tree /);
    count_ngram(\%counts, qw/ under a tree /);
    
    use Data::Dump;
    dd \%counts;
    
    sub count_ngram {
      my ($href, @ngram) = @_;
      my $final = pop @ngram;
      $href = $href->{$_} //= {} for @ngram;
      ++$href->{$final}{_COUNT};
    }
    

    输出

    {
      a => { rock => { _COUNT => 1 }, tree => { _COUNT => 1 } },
      under => {
        a => { _COUNT => 1, rock => { _COUNT => 1 }, tree => { _COUNT => 1 } },
      },
    }
    

    【讨论】:

    • 这个看起来比我建议的要干净得多。
    猜你喜欢
    • 2013-12-30
    • 1970-01-01
    • 2017-09-27
    • 2022-01-05
    • 1970-01-01
    • 2017-01-04
    • 2012-09-11
    • 1970-01-01
    • 2012-03-07
    相关资源
    最近更新 更多