通过perl哈希递归的正确方法是什么?

时间:2012-01-05 19:50:16

标签: perl recursion

我正在使用Config :: General来加载脚本的设置数据。作为其中的一部分,我希望脚本能够对配置数据中的值进行一些查找和替换。由于值可以嵌套多个级别,似乎最好的方法是使用递归函数来进行替换。我提出以下似乎工作正常:

#!/usr/bin/perl -w

use strict;
use Config::General;
use YAML::XS;

### Load in the config data and move it into the hash.
my $configObj = new Config::General(-ConfigFile => \*DATA);
my %config_hash = $configObj->getall;

### define the year to use as the replacement
my $current_year = (localtime())[5] + 1900;

### Call the function to update the data.
recursive_hash_replacement(\%config_hash, $current_year);


sub recursive_hash_replacement {

    ### Pull in the hash ref.
    my $tmp_hash_ref = shift;
    my $replacment_year = shift;


    ### Loop through all the keys in the hash ref.
    for my $tmp_hash_key (keys %{$tmp_hash_ref}) {

        ### If the value is another hash ref, call the function recursively.
        if(ref $tmp_hash_ref->{$tmp_hash_key} eq ref {}) {
            recursive_hash_replacement($tmp_hash_ref->{$tmp_hash_key}, $replacment_year);
        }

        ### otherwise, just update the value.
        else {
            $tmp_hash_ref->{$tmp_hash_key} =~ s{!YEAR!}{$replacment_year}g;
        }

    }
}


### Show the output with the updated values.
print Dump \%config_hash;


### Define the config data locally for testing.
__DATA__

key1 = /path/with/no/update
key2 = /path/with/new/!YEAR!/update

<level1>
    <level2>
        key3 = /another/!YEAR!/update
    </level2>
</level1>

有更好的方法吗?而且,更重要的是,这段代码中是否有潜伏等待咬我的东西?

2 个答案:

答案 0 :(得分:2)

您可以通过处理哈希值而不是键来简化一些事情。你还想测试其他类型的引用,因为在其他ref类型上盲目运行正则表达式可能不会做你想要的。

sub recursive_hash_replacement {

    ### unpack multiple args with list assignment:
    my ($hash, $year) = @_;

    ### Loop through all the values in the hash ref.
    for my $value (values %$hash) {

        ### If the value is another hash ref, call the function recursively.
        if (ref $value) {
            if (ref $value eq 'HASH') {
                 recursive_hash_replacement($value, $year);
            }
            else {
                 # handle other reftypes, or skip them, or throw an error
                 die "non hash reference: $value"
            }
        }
        ### otherwise, just update the value.
        else {
            $value =~ s{!YEAR!}{$year}g;
        }

    }
}

最后,在使用strict运行时运行良好,使用strictwarnings运行会更好,并且会捕获更多潜在错误。最后,您有时会遇到间接对象语法的问题,因此对构造函数使用标准方法调用:

Config::General->new(-ConfigFile => \*DATA);

或者如果你喜欢间接对象语法,可以通过在包名末尾添加::来明确它:

new Config::General:: -ConfigFile => \*DATA;

答案 1 :(得分:0)

值得一看CPAN上的Data::Visitor。这是Config::JFDI在幕后使用它进行替换的原因。