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};
精彩评论