Perl, (easier) access to hash variables
I have created a subfunction that returns a reference to a hash of attributes containing 7-8 variables. Whenever I want to access an element I either redefine it my $n_players = $ref_attr->{'n_players'}
or I call it directly $ref_attr->{'n_players'}
. I would prefer开发者_如何学C to use all the variables redefined (the first case) however this takes 7-8 lines of initializations and clutters my code up.
Is there a module (that I can't seem to find) or a nice method of exporting the hash keys for use as variables?
You can use hash slices to reduce the repetition involved in getting several entries from a hash. You can use this when initializing your copies, for example (assuming you also have a n_teams
entry):
my ($n_players, $n_teams) = @$ref_attr{qw( n_players n_teams )};
As Eric Strom suggested, you can also use indentation to help ensure you have the keys in the right order:
my ($n_players, $n_teams) = @$ref_attr{
qw( n_players n_teams)
};
As the others have said, it's possible to manipulate the symbol table to create a variable corresponding to each key, but it's not a good idea.
You can do this easily with references and hash slices:
sub takes_hashref {
my $hashref = shift;
my ($one, $two, $three, $four, $five) = \@$hashref{
qw(one two three four five)
};
print "one: $$one, two: $$two, three: $$three, ",
"four: $$four, five: $$five\n";
$_++ for $$one, $$three, $$five;
}
my %hash = qw(one 1 two 2 three 3 four 4 five 5);
takes_hashref \%hash; # one: 1, two: 2, three: 3, four: 4, five: 5
takes_hashref \%hash; # one: 2, two: 2, three: 4, four: 4, five: 6
takes_hashref \%hash; # one: 3, two: 2, three: 5, four: 4, five: 7
The @$hashref{qw(x y z)}
is a hash slice, which returns a list of the values. Prepending \
takes a reference to each value and returns the list of references. This is then assigned to your lexical variables. When you use each variable, double the sigil to dereference it: $$var
. Since these variables contain references to the hash fields, you can assign to them: $$var = 'new val'
, and the assignment will change the original hash. If you don't care about changes to the variables propagating back to the hash, you can omit the \
on the hash slice, and the double sigil on all the variables.
If you just want to create a shorter alias to work with, you can localize %_
in your sub:
sub takes_hashref2 {
my $hashref = shift;
local *_ = \%$hashref;
print "one: $_{one}, two: $_{two}, three: $_{three}, ",
"four: $_{four}, five: $_{five}\n";
$_++ for @_{qw(one three five)};
}
my %hash = qw(one 1 two 2 three 3 four 4 five 5);
takes_hashref2 \%hash; # one: 1, two: 2, three: 3, four: 4, five: 5
takes_hashref2 \%hash; # one: 2, two: 2, three: 4, four: 4, five: 6
takes_hashref2 \%hash; # one: 3, two: 2, three: 5, four: 4, five: 7
The line local *_ = \%$hashref
aliases %_
to the hash in $hashref
. The \%
portion is not necessary, but will throw an error if the value in $hashref
is not a hash. Its just a shorter way of writing:
die 'not a hash' unless ref $hashref eq 'HASH';
local *_ = $hashref;
And you can write it in one line as:
local *_ = \%{shift @_};
Don't. Either copy them explicitly (your first option), or just use the hash (your second option). That is so much clearer in the long run.
...
but if you really want to, the hash %:: is your current package variable hash (yes, perl internals). You can play with it.
perl -e'print %::';
I second Harmen's suggestion (don't), but in case you want it badly:
my %vars = ( a => 1, b => 2 );
while (my ($k,$v) = each %vars) {
$::{$k} = \$v;
}
print "a=$a, b=$b\n";
Another option would be to create a class for your collection of data and have the subfunction return an instance (object) of that class instead of a plain hashref. You would then access the values as $returned_object->n_players
. In this particular case, it doesn't really buy you much in terms of saved typing or cleaner appearance vs. $ref_attr->{n_players}
, but going OOP opens up a lot of possibilities (e.g., read-only or calculated attributes) which may or may not prove useful in the future as your program continues to evolve.
精彩评论