散列哈希哈希值的哈希值是否有一个“简单”的方法来获取列表末尾的元素?

时间:2012-11-15 17:58:43

标签: perl hash-of-hashes

我有一个Perl散列哈希值...大约11或12个元素深。请原谅我没有重复下面的结构!

某些级别具有固定标签,例如'NAMES''AGES'或类似的,所以访问这些级别很好,因为我可以直接使用标签,但我需要循环其他变量,这会产生一些非常长的语句。这是一组循环的一半的例子:

foreach my $person (sort keys %$people) {
        foreach my $name (sort keys %{$people->{$person}{'NAMES'}}) {
            foreach my $age (sort keys %{$people->{$person}{'NAMES'}{$name}{'AGES'}}) {
                . . . # and so on until I get to the push @list,$element; part

这只是一个例子,但它遵循我所拥有的结构。没有固定名称部分(大写字母中的元素)可能会更短,但是出于参考目的需要它们。

我尝试将元素转换为哈希,以便在每个阶段缩短它, 例如对于第二个foreach,我尝试了各种形式:

foreach my $name (sort keys %{$person->{'NAMES'}})

但这不起作用。我确定我以前见过类似的东西,所以语义可能不正确。

我研究了关于散列哈希的页面以及对哈希及其元素的引用等等,但没有运气。我已经看到了while each循环的例子,但它们似乎没有特别简短或更容易实现。也许只有一种不同的方法可以做到这一点,我忽略了这一点。我已经写出了一整套foreach循环,如果我不必再重复六次左右就会很好。

当然,可能没有“简单”的方式,但所有人都很感激!

4 个答案:

答案 0 :(得分:6)

$person是关键,要缩短内部循环的内容,需要将值赋给某些东西:

foreach my $person_key (sort keys %$people) {
    my $person = $people->{$person_key};
    my $names  = $person->{NAMES};
    foreach my $name (sort keys %$names) {

答案 1 :(得分:5)

此外,您还可以使用每个关键字。这肯定应该有所帮助。

while( my ($person, $val1) = each(%$people) ) {
    while( my ($name, $val2) = each(%$val1) ) {
        while( my ($age, $val3) = each(%$val2) ) {
            print $val3->{Somekey};

答案 2 :(得分:4)

如果要构建更灵活的解决方案,可以递归遍历数据树。考虑这个示例数据树(任意深度):

示例数据

my %people = (
    memowe => {
        NAMES => {
            memo        => {AGE => 666},
            we          => {AGE => 667},
        },
    },
    bladepanthera => {
        NAMES => {
            blade       => {AGE => 42},
            panthera    => {AGE => 17},
        },
    },
);

从你的问题我得出结论,你只想在叶子上工作(在这种情况下是AGE)。因此,可以编写递归traverse子例程,该子例程在按键排序的深度优先顺序中可能找到的所有叶子上执行给定的子参数。为方便起见,此subref获取离开本身和散列键路径:

制剂

sub traverse (&$@) {
    my ($do_it, $data, @path) = @_;

    # iterate
    foreach my $key (sort keys %$data) {

        # handle sub-tree
        if (ref($data->{$key}) eq 'HASH') {
            traverse($do_it, $data->{$key}, @path, $key);
            next;
        }

        # handle leave
        $do_it->($data->{$key}, @path, $key);
    }
}

我认为很明显这个人是如何使用内联评论的。如果你愿意的话,在所有节点上执行coderef并不是只有叶子,这不会有什么大的改变。请注意,为方便起见,我在此处特别添加了原型,因为使用众所周知的traversemap语法非常容易使用grep

在您的数据上执行内容

traverse { say shift . " (@_)" } \%people;

另请注意,它适用于哈希引用,我们使用隐式空列表初始化@path

<强>输出

42 (bladepanthera NAMES blade AGE)
17 (bladepanthera NAMES panthera AGE)
666 (memowe NAMES memo AGE)
667 (memowe NAMES we AGE)

给定的子程序(写为{ block })可以对给定的数据执行任何操作。例如,这个更易读的推送子程序:

my @flattened_people = ();

traverse {
    my ($thing, @path) = @_;
    push @flattened_people, { age => $thing, path => \@path };
} \%people;

答案 3 :(得分:4)

您可以使用Data::Walk,这是一种File :: Find for data structures。