【问题标题】:Access hash reference data using strings representing the hash structure使用表示散列结构的字符串访问散列引用数据
【发布时间】:2010-08-26 05:35:09
【问题描述】:

假设我有一个复杂的哈希引用 $hash_ref,我想通过执行以下操作来访问其中的数据:

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

当然,这不起作用,但我希望它能解释我想做的事情。

显然,我可以通过多种方式完成这项工作,但我(出于好奇)真的很想知道是否有一些 Perl 魔法可以在不拆分字符串等情况下完成这项工作。

我知道我可以创建 3 个字符串 ("books", "31335", "book_name") 并在一秒钟内完成,当然还有其他方法,但我不明白是否可以通过使用表示哈希结构的字符串来访问哈希数据,例如上面的例子。

谢谢:)

【问题讨论】:

  • 如果你尝试一下会发生什么?理论上,哈希键可以是任何字符串 ....
  • 得到一个错误:不能在 unblessed 参考上调用方法“{books}”

标签: perl hash


【解决方案1】:

可以使用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

【讨论】:

  • 非常感谢您的回答。我也很欣赏其他答案,但这确实回答了我想知道的一切。我不认为这是可能的,但我完全忘记了eval。现在我知道它是可以做到的(虽然我不会这样做:)。再次感谢。
【解决方案2】:

我绝不是说这是一个好主意,但这里是如何做到的(没有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'

【讨论】:

  • 非常感谢这个答案。我只需要先解密它,但我已经到了:) 再次感谢。
  • @sentinel => 你有什么特别想解释的吗?
【解决方案3】:

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

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";

【讨论】:

    【解决方案4】:
    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"
    

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        猜你喜欢
        • 2013-02-05
        • 2011-12-25
        • 2019-05-25
        • 1970-01-01
        • 2013-01-29
        • 2020-03-17
        • 2015-11-16
        • 2011-09-30
        • 1970-01-01
        相关资源
        最近更新 更多