开发者

Is it possible to safely access data in a nested data structure like Template Toolkit does?

Is there a module that provides functionality like开发者_StackOverflow Template Toolkit does when accessing a deeply nested data structure? I want to pull out something like $a = $hash{first}[0]{second}{third}[3] without having to test each part of the structure to see if it conforms to what I expect. If %hash = {} I want $a = undef, not produce an error.


Perl will do exactly what you described

This feature is called autovivification. Which means that container objects will spring into existence as soon as you use them. This holds as long as you don't violate any precedent you set yourself.

For example, trying to dereference something as a hash when you have already used it as an array reference is an error. More generally, if the value is defined, it can only be dereferenced as a particular type if it contains a reference to that type.

If you want protection against misuse as well, you can wrap the nested lookup in an eval block:

my $x = eval{ $hash{first}[0]{second}{third}[3] };

This will return undef if the eval fails. Note that this is NOT a string eval, which would be written eval '....';. In block form, Perl's eval is like the try {...} construct in other languages.

To determine if the eval failed or if the value in that position really is undef, test to see if the special variable $@ is true. If so, the eval failed, and the reason will be in $@. That would be written:

my $x = eval{ $hash{first}[0]{second}{third}[3] };

if (!$x and $@) { die "nested dereference failed: $@" }

Or you can use the module Try::Tiny which abstracts away the implementation details and protects against a few edge cases:

use Try::Tiny;

my $x;
try {
    $x = $hash{first}[0]{second}{third}[3];
} catch {
    die "nested dereference failed: $_";
};


Your error likely comes from wrong level of indirection, not because you don't have a value. Note that your hash variable is a scalar reference to hash, not a hash. So it should be defined as $hash = {}, not %hash = {}. Then, you access the elements there as $hash->{first}, not $hash{first}. And so on. If you define hash properly and try something like $hash->{first}->[0]->{second}->{third}->[3], you will get exactly undef, as you wanted, no errors.

Note: always use strict!


Check out Data::Diver.

You can access an arbitrary nested structure by key name (it doesn't matter if a layer is a hash or array). The Dive() subroutine will return an empty list if there is an error or it will return a matching value.

use strict;
use warnings;

use Data::Diver qw( Dive );

my $a = Dive( \%hash, 'first', 0, 'second', 'third', 3 );

if( defined $a ) {
    print "Got '$a'.\n";
}
else {
    print "Got no match.\n";
}


Something like this?

use strict;
use warnings;

my %hash;
my $elem = _eval( '$hash{first}[0]{second}{third}[3]' );

sub _eval {return (eval shift) // undef}

Of course you might as well do:

my $elem = eval {$hash{first}[0]{second}{third}[3] // undef};
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜