使用表示散列结构的字符串访问散列引用数据

时间:2010-08-25 11:23:55

标签: perl hash

我们假设我有一个复杂的哈希引用 $ hash_ref ,我想通过这样的方式访问其中的数据:

my $string1 = "{books}";
my $string2 = "{31335}->{book_name}";
print Dumper($hash_ref->$string1->$string2);

当然,这不起作用,但我希望它能解释我想做什么。

显然,我有很多方法可以完成这项工作,但我(出于好奇心)真的有兴趣弄清楚是否有一些Perl魔法可以使这项工作没有分裂字符串等。

我知道我可以创建3个字符串("books", "31335", "book_name")并在一秒钟内完成此操作,并且肯定有其他方法,但我从未理解是否可以通过使用表示哈希结构的字符串实际访问哈希数据,就像上面的例子一样。

谢谢:)

5 个答案:

答案 0 :(得分:6)

可以使用eval完成。但是,仅仅因为某些可以完成并不意味着应该

use strict;
use warnings;

my $hr = { books => { 31335 => { book_name => 'FOO' } } };

my $k1 = "{books}";
my $k2 = "{31335}->{book_name}";

my $f = eval "\$hr->$k1->$k2";  # Don't do this. It's a terrible idea.
print $f, "\n";                 # FOO

你应该咬紧牙关并从字符串中提取密钥:

my @ks = "$k1$k2" =~ /\{ \s* (.+?) \s* \}/gx;
$f = $hr;
$f = $f->{$_} for @ks;
print $f, "\n";                 # FOO

答案 1 :(得分:1)

我绝不是说这是一个好主意,但这里是如何做到的(没有eval):

use strict;
use warnings;

my $hash_ref = {books => {31335 => {book_name => 'perl'}}};

my $key = sub {
    my $hash = shift;
    my @keys = grep {s!^\{|\}$!!g; $_} split /->/ => "@_";
    $hash = $$hash{$_} for @keys;
    $hash
};

my $string1 = "{books}";
my $string2 = "{31335}->{book_name}";

print $hash_ref->$key($string1)->$key($string2);  # prints 'perl'

或者,为了使调用代码更加清晰,您可以编写一个装箱类来处理任意字符串作为方法调用:

sub key_methods {bless \$_[0] => 'KeyMethods'}

{package KeyMethods;
    use overload nomethod => sub {${$_[0]}},  # automatic unboxing
                    '%{}' => sub {${$_[0]}};
    sub AUTOLOAD {
        my $ret = ${$_[0]}->$key(our $AUTOLOAD =~ /([^:]+)$/);

        ref $ret eq 'HASH'
            ? bless \$ret
            : $ret;
    }
}

print key_methods($hash_ref)->$string1->$string2;  # prints 'perl'

答案 2 :(得分:0)

如果您更感兴趣的是变量存储用于访问数据结构的“路径”,而不是能够使用可以由用户提交或动态生成的字符串,那么您可以使用左值匿名子

my $deref1 = sub :lvalue { $_[0]->{books} };
my $deref2 = sub :lvalue { shift->{31335}{book_name} }; # if you don't like $_[0]

my $hash_ref = { };

# :lvalue on the sub allow it to be assigned to
$hash_ref->$deref1 = { 31335 => { book_name => 'FOO' } };

print $hash_ref->$deref1->$deref2, "\n";

答案 3 :(得分:-1)

my $string1 = "{books}";
my $string2 = "{31335}->{book_name}";
my $hash_ref = { key1 => { books =>  { 31335 =>  { book_name => "gold" }}}};
my $hash_ref_name = "\$hash_ref";
my $str = join("->", $hash_ref_name, "{key1}", $string1, $string2);
print eval $str, "\n"

答案 4 :(得分:-3)

AFAIK没有可用的东西可以做到这一点,你必须编写一个包装代码才能使它成为可能。我认为哈希实现和函数非常简单和酷,你可以使它在非常少的代码中工作。