Доступ к ссылочным данным хеша с использованием строк, представляющих структуру хеша - PullRequest
2 голосов
/ 25 августа 2010

Давайте предположим, что у меня есть сложная ссылка на хеш $ hash_ref , и я хотел бы получить доступ к данным в ней, выполнив что-то вроде этого:

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

Конечно, это не работает, но я надеюсь, что это объясняет, что я хотел бы сделать.

Очевидно, есть много способов сделать эту работу, но мне (из любопытства) действительно интересно выяснить, есть ли какая-то магия Perl, которая могла бы сделать эту работу без разделения строк и т. Д.

Я знаю, что я мог бы создать 3 строки ("books", "31335", "book_name") и сделать это за секунду, и, конечно, есть другие способы, но я никогда не понимал, можно ли на самом деле получить доступ к хеш-данным, используя строки, которые представляют хеш-структуру, как в приведенный выше пример.

Спасибо:)

Ответы [ 5 ]

6 голосов
/ 25 августа 2010

Это можно сделать с помощью 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 голос
/ 25 августа 2010

Я ни в коем случае не говорю, что это хорошая идея, но вот как это сделать (без 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'
0 голосов
/ 26 августа 2010

Если вас больше интересует наличие в переменной переменной «пути» для доступа к структуре данных, а не возможность использовать строку, которая может быть отправлена ​​пользователем или динамически сгенерирована, тогда вы можете использовать анонимную подпрограмму lvalue .

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";
0 голосов
/ 25 августа 2010
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"
0 голосов
/ 25 августа 2010

AFAIK, нет уже доступной вещи, которая могла бы сделать это, вы должны написать заключительный код, чтобы сделать это возможным. Я думаю, что реализация и функции хеша действительно просты и круты, вы можете заставить их работать в очень меньшем количестве кода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...